From 355755e690f1c0faf590e61ce672f470d96341ef Mon Sep 17 00:00:00 2001
From: Daniel Gultsch <daniel@gultsch.de>
Date: Fri, 24 Jan 2025 18:26:30 +0100
Subject: [PATCH] get rid of jid escaping api

Conversations never supported Jid Escaping but the underlaying library
did so we had API for that which could lead to confusion
---
 .../entities/AccountConfiguration.java        |   20 +-
 .../services/ImportBackupService.java         |   27 +-
 .../ui/EasyOnboardingInviteActivity.java      |    7 +-
 .../conversations/ui/MagicCreateActivity.java |   18 +-
 .../ui/ManageAccountActivity.java             |   18 +-
 .../conversations/ui/WelcomeActivity.java     |   18 +-
 .../utils/ProvisioningUtils.java              |   12 +-
 .../conversations/utils/SignupUtils.java      |   16 +-
 .../conversations/crypto/sasl/External.java   |    6 +-
 .../siacs/conversations/entities/Account.java |   22 +-
 .../conversations/entities/Bookmark.java      |  521 ++++---
 .../siacs/conversations/entities/Contact.java |   82 +-
 .../conversations/entities/Conversation.java  |    3 +-
 .../conversations/entities/MucOptions.java    |  105 +-
 .../conversations/entities/RawBlockable.java  |   17 +-
 .../conversations/entities/Reaction.java      |    8 +-
 .../eu/siacs/conversations/entities/Room.java |   16 +-
 .../conversations/generator/IqGenerator.java  |  102 +-
 .../conversations/parser/AbstractParser.java  |  333 ++--
 .../siacs/conversations/parser/IqParser.java  |  186 ++-
 .../conversations/parser/MessageParser.java   |   33 +-
 .../conversations/parser/PresenceParser.java  |   32 +-
 .../persistance/DatabaseBackend.java          |    9 +-
 .../persistance/UnifiedPushDatabase.java      |   39 +-
 .../conversations/services/AvatarService.java | 1347 +++++++++--------
 .../services/CallIntegration.java             |    2 +-
 .../CallIntegrationConnectionService.java     |   22 +-
 .../services/ChannelDiscoveryService.java     |   33 +-
 .../services/NotificationService.java         |   14 +-
 .../services/ShortcutService.java             |   17 +-
 .../services/UnifiedPushBroker.java           |   69 +-
 .../services/XmppConnectionService.java       |   19 +-
 .../conversations/ui/BlockContactDialog.java  |  148 +-
 .../conversations/ui/BlocklistActivity.java   |  180 +--
 .../ui/ChannelDiscoveryActivity.java          |    2 +-
 ...hooseAccountForProfilePictureActivity.java |   25 +-
 .../ui/ChooseContactActivity.java             |  119 +-
 .../ui/ConferenceDetailsActivity.java         |    9 +-
 .../ui/ContactDetailsActivity.java            |   14 +-
 .../ui/ConversationFragment.java              |    9 +-
 .../ui/ConversationsOverviewFragment.java     |  740 ++++-----
 .../ui/CreatePublicChannelDialog.java         |  169 ++-
 .../conversations/ui/EditAccountActivity.java |   46 +-
 .../conversations/ui/EnterJidDialog.java      |   35 +-
 .../ui/MediaBrowserActivity.java              |   29 +-
 .../ui/PublishProfilePictureActivity.java     |    6 +-
 .../conversations/ui/RtpSessionActivity.java  |   53 +-
 .../conversations/ui/ShareWithActivity.java   |    2 +-
 .../ui/StartConversationActivity.java         |   50 +-
 .../conversations/ui/TrustKeysActivity.java   |  924 +++++------
 .../conversations/ui/UriHandlerActivity.java  |   30 +-
 .../siacs/conversations/ui/XmppActivity.java  |    8 +-
 .../ui/adapter/AccountAdapter.java            |   46 +-
 .../ui/adapter/KnownHostsAdapter.java         |  103 +-
 .../fragment/settings/UpSettingsFragment.java |   48 +-
 .../conversations/utils/AccountUtils.java     |   18 +-
 .../conversations/utils/BackupFileHeader.java |   38 +-
 .../conversations/utils/CryptoHelper.java     |   99 +-
 .../utils/IrregularUnicodeDetector.java       |  412 ++---
 .../siacs/conversations/utils/JidHelper.java  |   18 +-
 .../eu/siacs/conversations/utils/XmppUri.java |   44 +-
 .../worker/ExportBackupWorker.java            |   14 +-
 .../eu/siacs/conversations/xml/Element.java   |   24 +-
 .../siacs/conversations/xmpp/InvalidJid.java  |  155 --
 .../java/eu/siacs/conversations/xmpp/Jid.java |  353 ++++-
 .../siacs/conversations/xmpp/WrappedJid.java  |  130 --
 .../conversations/xmpp/XmppConnection.java    |    6 +-
 .../xmpp/jingle/JingleConnectionManager.java  |   27 +-
 .../xmpp/jingle/JingleRtpConnection.java      |  126 +-
 .../transports/SocksByteStreamsTransport.java |   22 +-
 .../android/xmpp/model/bind/Bind.java         |    3 +-
 .../model/sasl2/AuthorizationIdentifier.java  |    6 +-
 .../android/xmpp/model/stanza/Stanza.java     |    5 +-
 73 files changed, 3852 insertions(+), 3616 deletions(-)
 delete mode 100644 src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java
 delete mode 100644 src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java

diff --git a/src/conversations/java/eu/siacs/conversations/entities/AccountConfiguration.java b/src/conversations/java/eu/siacs/conversations/entities/AccountConfiguration.java
index 65b6804f9..c3326d8ee 100644
--- a/src/conversations/java/eu/siacs/conversations/entities/AccountConfiguration.java
+++ b/src/conversations/java/eu/siacs/conversations/entities/AccountConfiguration.java
@@ -5,7 +5,6 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonSyntaxException;
 import com.google.gson.annotations.SerializedName;
-
 import eu.siacs.conversations.xmpp.Jid;
 
 public class AccountConfiguration {
@@ -17,7 +16,7 @@ public class AccountConfiguration {
     public String password;
 
     public Jid getJid() {
-        return Jid.ofEscaped(address);
+        return Jid.of(address);
     }
 
     public static AccountConfiguration parse(final String input) {
@@ -27,24 +26,17 @@ public class AccountConfiguration {
         } catch (JsonSyntaxException e) {
             throw new IllegalArgumentException("Not a valid JSON string", e);
         }
-        Preconditions.checkArgument(
-                c.protocol == Protocol.XMPP,
-                "Protocol must be XMPP"
-        );
+        Preconditions.checkArgument(c.protocol == Protocol.XMPP, "Protocol must be XMPP");
         Preconditions.checkArgument(
                 c.address != null && c.getJid().isBareJid() && !c.getJid().isDomainJid(),
-                "Invalid XMPP address"
-        );
+                "Invalid XMPP address");
         Preconditions.checkArgument(
-                c.password != null && c.password.length() > 0,
-                "No password specified"
-        );
+                c.password != null && !c.password.isEmpty(), "No password specified");
         return c;
     }
 
     public enum Protocol {
-        @SerializedName("xmpp") XMPP,
+        @SerializedName("xmpp")
+        XMPP,
     }
-
 }
-
diff --git a/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java b/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java
index dd96468ee..c15208bfa 100644
--- a/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java
+++ b/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java
@@ -16,10 +16,8 @@ import android.os.Binder;
 import android.os.IBinder;
 import android.provider.OpenableColumns;
 import android.util.Log;
-
 import androidx.core.app.NotificationCompat;
 import androidx.core.app.NotificationManagerCompat;
-
 import com.google.common.base.Charsets;
 import com.google.common.base.Stopwatch;
 import com.google.common.io.CountingInputStream;
@@ -27,7 +25,6 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
@@ -41,14 +38,6 @@ import eu.siacs.conversations.utils.BackupFileHeader;
 import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
 import eu.siacs.conversations.worker.ExportBackupWorker;
 import eu.siacs.conversations.xmpp.Jid;
-
-import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.io.CipherInputStream;
-import org.bouncycastle.crypto.modes.AEADBlockCipher;
-import org.bouncycastle.crypto.modes.GCMBlockCipher;
-import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.File;
@@ -72,8 +61,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.ZipException;
-
 import javax.crypto.BadPaddingException;
+import org.bouncycastle.crypto.engines.AESEngine;
+import org.bouncycastle.crypto.io.CipherInputStream;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
+import org.bouncycastle.crypto.modes.GCMBlockCipher;
+import org.bouncycastle.crypto.params.AEADParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
 
 public class ImportBackupService extends Service {
 
@@ -314,10 +308,11 @@ public class ImportBackupService extends Service {
             final Jid jid = backupFileHeader.getJid();
             final Cursor countCursor =
                     db.rawQuery(
-                            "select count(messages.uuid) from messages join conversations on conversations.uuid=messages.conversationUuid join accounts on conversations.accountUuid=accounts.uuid where accounts.username=? and accounts.server=?",
-                            new String[] {
-                                jid.getEscapedLocal(), jid.getDomain().toEscapedString()
-                            });
+                            "select count(messages.uuid) from messages join conversations on"
+                                + " conversations.uuid=messages.conversationUuid join accounts on"
+                                + " conversations.accountUuid=accounts.uuid where"
+                                + " accounts.username=? and accounts.server=?",
+                            new String[] {jid.getLocal(), jid.getDomain().toString()});
             countCursor.moveToFirst();
             final int count = countCursor.getInt(0);
             Log.d(
diff --git a/src/conversations/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java b/src/conversations/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java
index 9228a5170..874557198 100644
--- a/src/conversations/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java
+++ b/src/conversations/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java
@@ -11,13 +11,10 @@ import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.android.material.color.MaterialColors;
 import com.google.common.base.Strings;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityEasyInviteBinding;
@@ -153,7 +150,7 @@ public class EasyOnboardingInviteActivity extends XmppActivity
         }
         final Intent launchIntent = getIntent();
         final String accountExtra = launchIntent.getStringExtra(EXTRA_ACCOUNT);
-        final Jid jid = accountExtra == null ? null : Jid.ofEscaped(accountExtra);
+        final Jid jid = accountExtra == null ? null : Jid.of(accountExtra);
         if (jid == null) {
             return;
         }
@@ -163,7 +160,7 @@ public class EasyOnboardingInviteActivity extends XmppActivity
 
     public static void launch(final Account account, final Activity context) {
         final Intent intent = new Intent(context, EasyOnboardingInviteActivity.class);
-        intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
+        intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
         context.startActivity(intent);
     }
 
diff --git a/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java b/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
index b6d4d452e..bebf9bab6 100644
--- a/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
+++ b/src/conversations/java/eu/siacs/conversations/ui/MagicCreateActivity.java
@@ -7,9 +7,7 @@ import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.View;
 import android.widget.Toast;
-
 import androidx.databinding.DataBindingUtil;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityMagicCreateBinding;
@@ -17,7 +15,6 @@ import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.InstallReferrerUtils;
 import eu.siacs.conversations.xmpp.Jid;
-
 import java.security.SecureRandom;
 
 public class MagicCreateActivity extends XmppActivity implements TextWatcher {
@@ -68,15 +65,15 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
                         final boolean fixedUsername;
                         if (this.domain != null && this.username != null) {
                             fixedUsername = true;
-                            jid = Jid.ofLocalAndDomainEscaped(this.username, this.domain);
+                            jid = Jid.ofLocalAndDomain(this.username, this.domain);
                         } else if (this.domain != null) {
                             fixedUsername = false;
-                            jid = Jid.ofLocalAndDomainEscaped(username, this.domain);
+                            jid = Jid.ofLocalAndDomain(username, this.domain);
                         } else {
                             fixedUsername = false;
-                            jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN);
+                            jid = Jid.ofLocalAndDomain(username, Config.MAGIC_CREATE_DOMAIN);
                         }
-                        if (!jid.getEscapedLocal().equals(jid.getLocal())
+                        if (!jid.getLocal().equals(jid.getLocal())
                                 || (this.username == null && username.length() < 3)) {
                             binding.usernameLayout.setError(getString(R.string.invalid_username));
                             binding.username.requestFocus();
@@ -146,12 +143,11 @@ public class MagicCreateActivity extends XmppActivity implements TextWatcher {
                 binding.fullJid.setVisibility(View.VISIBLE);
                 final Jid jid;
                 if (this.domain == null) {
-                    jid = Jid.ofLocalAndDomainEscaped(username, Config.MAGIC_CREATE_DOMAIN);
+                    jid = Jid.ofLocalAndDomain(username, Config.MAGIC_CREATE_DOMAIN);
                 } else {
-                    jid = Jid.ofLocalAndDomainEscaped(username, this.domain);
+                    jid = Jid.ofLocalAndDomain(username, this.domain);
                 }
-                binding.fullJid.setText(
-                        getString(R.string.your_full_jid_will_be, jid.toEscapedString()));
+                binding.fullJid.setText(getString(R.string.your_full_jid_will_be, jid.toString()));
                 binding.usernameLayout.setError(null);
             } catch (final IllegalArgumentException e) {
                 binding.fullJid.setVisibility(View.INVISIBLE);
diff --git a/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java
index 6ab32682f..c2a82f8d5 100644
--- a/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java
+++ b/src/conversations/java/eu/siacs/conversations/ui/ManageAccountActivity.java
@@ -17,13 +17,10 @@ import android.view.MenuItem;
 import android.view.View;
 import android.widget.AdapterView.AdapterContextMenuInfo;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.ActionBar;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.common.base.Strings;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityManageAccountsBinding;
@@ -34,12 +31,10 @@ import eu.siacs.conversations.ui.adapter.AccountAdapter;
 import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.XmppConnection;
-
-import org.openintents.openpgp.util.OpenPgpApi;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
+import org.openintents.openpgp.util.OpenPgpApi;
 
 public class ManageAccountActivity extends XmppActivity
         implements OnAccountUpdate,
@@ -95,7 +90,7 @@ public class ManageAccountActivity extends XmppActivity
             String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
             if (jid != null) {
                 try {
-                    this.selectedAccountJid = Jid.ofEscaped(jid);
+                    this.selectedAccountJid = Jid.of(jid);
                 } catch (IllegalArgumentException e) {
                     this.selectedAccountJid = null;
                 }
@@ -113,7 +108,7 @@ public class ManageAccountActivity extends XmppActivity
     public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
         if (selectedAccount != null) {
             savedInstanceState.putString(
-                    STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toEscapedString());
+                    STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toString());
         }
         super.onSaveInstanceState(savedInstanceState);
     }
@@ -132,7 +127,7 @@ public class ManageAccountActivity extends XmppActivity
             menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
             menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
         }
-        menu.setHeaderTitle(this.selectedAccount.getJid().asBareJid().toEscapedString());
+        menu.setHeaderTitle(this.selectedAccount.getJid().asBareJid().toString());
     }
 
     @Override
@@ -297,7 +292,7 @@ public class ManageAccountActivity extends XmppActivity
 
     private void publishAvatar(Account account) {
         Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
-        intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
+        intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
         startActivity(intent);
     }
 
@@ -357,7 +352,8 @@ public class ManageAccountActivity extends XmppActivity
             Log.d(
                     Config.LOGTAG,
                     account.getJid().asBareJid()
-                            + ": quick start disabled. account will regain this capability on the next connect");
+                            + ": quick start disabled. account will regain this capability on the"
+                            + " next connect");
         }
         if (!xmppConnectionService.updateAccount(account)) {
             Toast.makeText(this, R.string.unable_to_update_account, Toast.LENGTH_SHORT).show();
diff --git a/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java b/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java
index b2a40976c..514d0ccfd 100644
--- a/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java
+++ b/src/conversations/java/eu/siacs/conversations/ui/WelcomeActivity.java
@@ -1,5 +1,8 @@
 package eu.siacs.conversations.ui;
 
+import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
+import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
+
 import android.Manifest;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
@@ -12,14 +15,10 @@ import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.databinding.DataBindingUtil;
-
-import java.util.Arrays;
-import java.util.List;
-
+import com.google.common.base.Strings;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityWelcomeBinding;
@@ -30,11 +29,8 @@ import eu.siacs.conversations.utils.InstallReferrerUtils;
 import eu.siacs.conversations.utils.SignupUtils;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
-
-import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
-import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
-
-import com.google.common.base.Strings;
+import java.util.Arrays;
+import java.util.List;
 
 public class WelcomeActivity extends XmppActivity
         implements XmppConnectionService.OnAccountCreated, KeyChainAliasCallback {
@@ -196,7 +192,7 @@ public class WelcomeActivity extends XmppActivity
     @Override
     public void onAccountCreated(final Account account) {
         final Intent intent = new Intent(this, EditAccountActivity.class);
-        intent.putExtra("jid", account.getJid().asBareJid().toEscapedString());
+        intent.putExtra("jid", account.getJid().asBareJid().toString());
         intent.putExtra("init", true);
         addInviteUri(intent);
         startActivity(intent);
diff --git a/src/conversations/java/eu/siacs/conversations/utils/ProvisioningUtils.java b/src/conversations/java/eu/siacs/conversations/utils/ProvisioningUtils.java
index 593291d95..fdfe28dbb 100644
--- a/src/conversations/java/eu/siacs/conversations/utils/ProvisioningUtils.java
+++ b/src/conversations/java/eu/siacs/conversations/utils/ProvisioningUtils.java
@@ -3,15 +3,13 @@ package eu.siacs.conversations.utils;
 import android.app.Activity;
 import android.content.Intent;
 import android.widget.Toast;
-
-import java.util.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.AccountConfiguration;
 import eu.siacs.conversations.persistance.DatabaseBackend;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.EditAccountActivity;
 import eu.siacs.conversations.xmpp.Jid;
+import java.util.List;
 
 public class ProvisioningUtils {
 
@@ -20,7 +18,8 @@ public class ProvisioningUtils {
         try {
             accountConfiguration = AccountConfiguration.parse(json);
         } catch (final IllegalArgumentException e) {
-            Toast.makeText(activity, R.string.improperly_formatted_provisioning, Toast.LENGTH_LONG).show();
+            Toast.makeText(activity, R.string.improperly_formatted_provisioning, Toast.LENGTH_LONG)
+                    .show();
             return;
         }
         final Jid jid = accountConfiguration.getJid();
@@ -31,13 +30,12 @@ public class ProvisioningUtils {
         }
         final Intent serviceIntent = new Intent(activity, XmppConnectionService.class);
         serviceIntent.setAction(XmppConnectionService.ACTION_PROVISION_ACCOUNT);
-        serviceIntent.putExtra("address", jid.asBareJid().toEscapedString());
+        serviceIntent.putExtra("address", jid.asBareJid().toString());
         serviceIntent.putExtra("password", accountConfiguration.password);
         Compatibility.startService(activity, serviceIntent);
         final Intent intent = new Intent(activity, EditAccountActivity.class);
-        intent.putExtra("jid", jid.asBareJid().toEscapedString());
+        intent.putExtra("jid", jid.asBareJid().toString());
         intent.putExtra("init", true);
         activity.startActivity(intent);
     }
-
 }
diff --git a/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java b/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
index fb088234a..b8123947f 100644
--- a/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
+++ b/src/conversations/java/eu/siacs/conversations/utils/SignupUtils.java
@@ -2,7 +2,6 @@ package eu.siacs.conversations.utils;
 
 import android.app.Activity;
 import android.content.Intent;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.services.XmppConnectionService;
@@ -21,13 +20,14 @@ public class SignupUtils {
         return true;
     }
 
-    public static Intent getTokenRegistrationIntent(final Activity activity, Jid jid, String preAuth) {
+    public static Intent getTokenRegistrationIntent(
+            final Activity activity, Jid jid, String preAuth) {
         final Intent intent = new Intent(activity, MagicCreateActivity.class);
         if (jid.isDomainJid()) {
-            intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain().toEscapedString());
+            intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain().toString());
         } else {
-            intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain().toEscapedString());
-            intent.putExtra(MagicCreateActivity.EXTRA_USERNAME, jid.getEscapedLocal());
+            intent.putExtra(MagicCreateActivity.EXTRA_DOMAIN, jid.getDomain().toString());
+            intent.putExtra(MagicCreateActivity.EXTRA_USERNAME, jid.getLocal());
         }
         intent.putExtra(MagicCreateActivity.EXTRA_PRE_AUTH, preAuth);
         return intent;
@@ -55,7 +55,9 @@ public class SignupUtils {
             intent = new Intent(activity, EditAccountActivity.class);
             intent.putExtra("jid", pendingAccount.getJid().asBareJid().toString());
             if (!pendingAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
-                intent.putExtra(EditAccountActivity.EXTRA_FORCE_REGISTER, pendingAccount.isOptionSet(Account.OPTION_REGISTER));
+                intent.putExtra(
+                        EditAccountActivity.EXTRA_FORCE_REGISTER,
+                        pendingAccount.isOptionSet(Account.OPTION_REGISTER));
             }
         } else {
             if (service.getAccounts().size() == 0) {
@@ -74,4 +76,4 @@ public class SignupUtils {
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         return intent;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java
index 6aba413a5..2e8adf189 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/External.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/External.java
@@ -1,10 +1,8 @@
 package eu.siacs.conversations.crypto.sasl;
 
 import android.util.Base64;
-
-import javax.net.ssl.SSLSocket;
-
 import eu.siacs.conversations.entities.Account;
+import javax.net.ssl.SSLSocket;
 
 public class External extends SaslMechanism {
 
@@ -27,6 +25,6 @@ public class External extends SaslMechanism {
     @Override
     public String getClientFirstMessage(final SSLSocket sslSocket) {
         return Base64.encodeToString(
-                account.getJid().asBareJid().toEscapedString().getBytes(), Base64.NO_WRAP);
+                account.getJid().asBareJid().toString().getBytes(), Base64.NO_WRAP);
     }
 }
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index ce13ba9f3..f55618fe2 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -268,7 +268,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
     }
 
     public String getUsername() {
-        return jid.getEscapedLocal();
+        return jid.getLocal();
     }
 
     public boolean setJid(final Jid next) {
@@ -292,7 +292,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
     }
 
     public String getServer() {
-        return jid.getDomain().toEscapedString();
+        return jid.getDomain().toString();
     }
 
     public String getPassword() {
@@ -508,7 +508,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
         final ContentValues values = new ContentValues();
         values.put(UUID, uuid);
         values.put(USERNAME, jid.getLocal());
-        values.put(SERVER, jid.getDomain().toEscapedString());
+        values.put(SERVER, jid.getDomain().toString());
         values.put(PASSWORD, password);
         values.put(OPTIONS, options);
         synchronized (this.keys) {
@@ -698,11 +698,11 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
 
     public String getShareableUri() {
         List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
-        String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString();
-        if (fingerprints.size() > 0) {
-            return XmppUri.getFingerprintUri(uri, fingerprints, ';');
-        } else {
+        final String uri = "xmpp:" + this.getJid().asBareJid().toString();
+        if (fingerprints.isEmpty()) {
             return uri;
+        } else {
+            return XmppUri.getFingerprintUri(uri, fingerprints, ';');
         }
     }
 
@@ -710,11 +710,11 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
         List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
         String uri =
                 "https://conversations.im/i/"
-                        + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
-        if (fingerprints.size() > 0) {
-            return XmppUri.getFingerprintUri(uri, fingerprints, '&');
-        } else {
+                        + XmppUri.lameUrlEncode(this.getJid().asBareJid().toString());
+        if (fingerprints.isEmpty()) {
             return uri;
+        } else {
+            return XmppUri.getFingerprintUri(uri, fingerprints, '&');
         }
     }
 
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index de7694717..2a0094828 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -1,13 +1,15 @@
 package eu.siacs.conversations.entities;
 
 import android.content.Context;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
-
+import eu.siacs.conversations.utils.StringUtils;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xml.Namespace;
+import eu.siacs.conversations.xmpp.Jid;
 import java.lang.ref.WeakReference;
 import java.util.Collections;
 import java.util.HashMap;
@@ -15,265 +17,260 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-import eu.siacs.conversations.utils.StringUtils;
-import eu.siacs.conversations.utils.UIHelper;
-import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xml.Namespace;
-import eu.siacs.conversations.xmpp.InvalidJid;
-import eu.siacs.conversations.xmpp.Jid;
-
 public class Bookmark extends Element implements ListItem {
 
-	private final Account account;
-	private WeakReference<Conversation> conversation;
-	private Jid jid;
-	protected Element extensions = new Element("extensions", Namespace.BOOKMARKS2);
-
-	public Bookmark(final Account account, final Jid jid) {
-		super("conference");
-		this.jid = jid;
-		this.setAttribute("jid", jid);
-		this.account = account;
-	}
-
-	private Bookmark(Account account) {
-		super("conference");
-		this.account = account;
-	}
-
-	public static Map<Jid, Bookmark> parseFromStorage(Element storage, Account account) {
-		if (storage == null) {
-			return Collections.emptyMap();
-		}
-		final HashMap<Jid, Bookmark> bookmarks = new HashMap<>();
-		for (final Element item : storage.getChildren()) {
-			if (item.getName().equals("conference")) {
-				final Bookmark bookmark = Bookmark.parse(item, account);
-				if (bookmark != null) {
-					final Bookmark old = bookmarks.put(bookmark.jid, bookmark);
-					if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) {
-						bookmark.setBookmarkName(old.getBookmarkName());
-					}
-				}
-			}
-		}
-		return bookmarks;
-	}
-
-	public static Map<Jid, Bookmark> parseFromPubSub(final Element pubSub, final Account account) {
-		if (pubSub == null) {
-			return Collections.emptyMap();
-		}
-		final Element items = pubSub.findChild("items");
-		if (items != null && Namespace.BOOKMARKS2.equals(items.getAttribute("node"))) {
-			final Map<Jid, Bookmark> bookmarks = new HashMap<>();
-			for(Element item : items.getChildren()) {
-				if (item.getName().equals("item")) {
-					final Bookmark bookmark = Bookmark.parseFromItem(item, account);
-					if (bookmark != null) {
-						bookmarks.put(bookmark.jid, bookmark);
-					}
-				}
-			}
-			return bookmarks;
-		}
-		return Collections.emptyMap();
-	}
-
-	public static Bookmark parse(Element element, Account account) {
-		Bookmark bookmark = new Bookmark(account);
-		bookmark.setAttributes(element.getAttributes());
-		bookmark.setChildren(element.getChildren());
-		bookmark.jid = InvalidJid.getNullForInvalid(bookmark.getAttributeAsJid("jid"));
-		if (bookmark.jid == null) {
-			return null;
-		}
-		return bookmark;
-	}
-
-	public static Bookmark parseFromItem(Element item, Account account) {
-		final Element conference = item.findChild("conference", Namespace.BOOKMARKS2);
-		if (conference == null) {
-			return null;
-		}
-		final Bookmark bookmark = new Bookmark(account);
-		bookmark.jid = InvalidJid.getNullForInvalid(item.getAttributeAsJid("id"));
-		// TODO verify that we only use bare jids and ignore full jids
-		if (bookmark.jid == null) {
-			return null;
-		}
-		bookmark.setBookmarkName(conference.getAttribute("name"));
-		bookmark.setAutojoin(conference.getAttributeAsBoolean("autojoin"));
-		bookmark.setNick(conference.findChildContent("nick"));
-		bookmark.setPassword(conference.findChildContent("password"));
-		final Element extensions = conference.findChild("extensions", Namespace.BOOKMARKS2);
-		if (extensions != null) {
-			bookmark.extensions = extensions;
-		}
-		return bookmark;
-	}
-
-	public Element getExtensions() {
-		return extensions;
-	}
-
-	public void setAutojoin(boolean autojoin) {
-		if (autojoin) {
-			this.setAttribute("autojoin", "true");
-		} else {
-			this.setAttribute("autojoin", "false");
-		}
-	}
-
-	@Override
-	public int compareTo(final @NonNull ListItem another) {
-		return this.getDisplayName().compareToIgnoreCase(
-				another.getDisplayName());
-	}
-
-	@Override
-	public String getDisplayName() {
-		final Conversation c = getConversation();
-		final String name = getBookmarkName();
-		if (c != null) {
-			return c.getName().toString();
-		} else if (printableValue(name, false)) {
-			return name.trim();
-		} else {
-			Jid jid = this.getJid();
-			return jid != null && jid.getLocal() != null ? jid.getLocal() : "";
-		}
-	}
-
-	public static boolean printableValue(@Nullable String value, boolean permitNone) {
-		return value != null && !value.trim().isEmpty() && (permitNone || !"None".equals(value));
-	}
-
-	public static boolean printableValue(@Nullable String value) {
-		return printableValue(value, true);
-	}
-
-	@Override
-	public Jid getJid() {
-		return this.jid;
-	}
-
-	public Jid getFullJid() {
-		final String nick = Strings.nullToEmpty(getNick()).trim();
-		if (jid == null || nick.isEmpty()) {
-			return jid;
-		}
-		try {
-			return jid.withResource(nick);
-		} catch (final IllegalArgumentException e) {
-			return jid;
-		}
-	}
-
-	@Override
-	public List<Tag> getTags(final Context context) {
-		final ImmutableList.Builder<Tag> tags = new ImmutableList.Builder<>();
-		for (final Element element : getChildren()) {
-			final String content = element.getContent();
-			if (Strings.isNullOrEmpty(content)) {
-				continue;
-			}
-			if (element.getName().equals("group")) {
-				tags.add(new Tag(content));
-			}
-		}
-		return tags.build();
-	}
-
-	public String getNick() {
-		return Strings.emptyToNull(this.findChildContent("nick"));
-	}
-
-	public void setNick(String nick) {
-		Element element = this.findChild("nick");
-		if (element == null) {
-			element = this.addChild("nick");
-		}
-		element.setContent(nick);
-	}
-
-	public boolean autojoin() {
-		return this.getAttributeAsBoolean("autojoin");
-	}
-
-	public String getPassword() {
-		return this.findChildContent("password");
-	}
-
-	public void setPassword(String password) {
-		Element element = this.findChild("password");
-		if (element != null) {
-			element.setContent(password);
-		}
-	}
-
-	@Override
-	public boolean match(Context context, String needle) {
-		if (needle == null) {
-			return true;
-		}
-		needle = needle.toLowerCase(Locale.US);
-		final Jid jid = getJid();
-		return (jid != null && jid.toString().contains(needle)) ||
-			getDisplayName().toLowerCase(Locale.US).contains(needle) ||
-			matchInTag(context, needle);
-	}
-
-	private boolean matchInTag(Context context, String needle) {
-		needle = needle.toLowerCase(Locale.US);
-		for (Tag tag : getTags(context)) {
-			if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	public Account getAccount() {
-		return this.account;
-	}
-
-	public synchronized Conversation getConversation() {
-		return this.conversation != null ? this.conversation.get() : null;
-	}
-
-	public synchronized void setConversation(Conversation conversation) {
-		if (this.conversation != null) {
-			this.conversation.clear();
-		}
-		if (conversation == null) {
-			this.conversation = null;
-		} else {
-			this.conversation = new WeakReference<>(conversation);
-		}
-	}
-
-	public String getBookmarkName() {
-		return this.getAttribute("name");
-	}
-
-	public boolean setBookmarkName(String name) {
-		String before = getBookmarkName();
-		if (name != null) {
-			this.setAttribute("name", name);
-		} else {
-			this.removeAttribute("name");
-		}
-		return StringUtils.changed(before, name);
-	}
-
-	@Override
-	public int getAvatarBackgroundColor() {
-		return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
-	}
-
-	@Override
-	public String getAvatarName() {
-		return getDisplayName();
-	}
+    private final Account account;
+    private WeakReference<Conversation> conversation;
+    private Jid jid;
+    protected Element extensions = new Element("extensions", Namespace.BOOKMARKS2);
+
+    public Bookmark(final Account account, final Jid jid) {
+        super("conference");
+        this.jid = jid;
+        this.setAttribute("jid", jid);
+        this.account = account;
+    }
+
+    private Bookmark(Account account) {
+        super("conference");
+        this.account = account;
+    }
+
+    public static Map<Jid, Bookmark> parseFromStorage(Element storage, Account account) {
+        if (storage == null) {
+            return Collections.emptyMap();
+        }
+        final HashMap<Jid, Bookmark> bookmarks = new HashMap<>();
+        for (final Element item : storage.getChildren()) {
+            if (item.getName().equals("conference")) {
+                final Bookmark bookmark = Bookmark.parse(item, account);
+                if (bookmark != null) {
+                    final Bookmark old = bookmarks.put(bookmark.jid, bookmark);
+                    if (old != null
+                            && old.getBookmarkName() != null
+                            && bookmark.getBookmarkName() == null) {
+                        bookmark.setBookmarkName(old.getBookmarkName());
+                    }
+                }
+            }
+        }
+        return bookmarks;
+    }
+
+    public static Map<Jid, Bookmark> parseFromPubSub(final Element pubSub, final Account account) {
+        if (pubSub == null) {
+            return Collections.emptyMap();
+        }
+        final Element items = pubSub.findChild("items");
+        if (items != null && Namespace.BOOKMARKS2.equals(items.getAttribute("node"))) {
+            final Map<Jid, Bookmark> bookmarks = new HashMap<>();
+            for (Element item : items.getChildren()) {
+                if (item.getName().equals("item")) {
+                    final Bookmark bookmark = Bookmark.parseFromItem(item, account);
+                    if (bookmark != null) {
+                        bookmarks.put(bookmark.jid, bookmark);
+                    }
+                }
+            }
+            return bookmarks;
+        }
+        return Collections.emptyMap();
+    }
+
+    public static Bookmark parse(Element element, Account account) {
+        Bookmark bookmark = new Bookmark(account);
+        bookmark.setAttributes(element.getAttributes());
+        bookmark.setChildren(element.getChildren());
+        bookmark.jid = Jid.Invalid.getNullForInvalid(bookmark.getAttributeAsJid("jid"));
+        if (bookmark.jid == null) {
+            return null;
+        }
+        return bookmark;
+    }
+
+    public static Bookmark parseFromItem(Element item, Account account) {
+        final Element conference = item.findChild("conference", Namespace.BOOKMARKS2);
+        if (conference == null) {
+            return null;
+        }
+        final Bookmark bookmark = new Bookmark(account);
+        bookmark.jid = Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("id"));
+        // TODO verify that we only use bare jids and ignore full jids
+        if (bookmark.jid == null) {
+            return null;
+        }
+        bookmark.setBookmarkName(conference.getAttribute("name"));
+        bookmark.setAutojoin(conference.getAttributeAsBoolean("autojoin"));
+        bookmark.setNick(conference.findChildContent("nick"));
+        bookmark.setPassword(conference.findChildContent("password"));
+        final Element extensions = conference.findChild("extensions", Namespace.BOOKMARKS2);
+        if (extensions != null) {
+            bookmark.extensions = extensions;
+        }
+        return bookmark;
+    }
+
+    public Element getExtensions() {
+        return extensions;
+    }
+
+    public void setAutojoin(boolean autojoin) {
+        if (autojoin) {
+            this.setAttribute("autojoin", "true");
+        } else {
+            this.setAttribute("autojoin", "false");
+        }
+    }
+
+    @Override
+    public int compareTo(final @NonNull ListItem another) {
+        return this.getDisplayName().compareToIgnoreCase(another.getDisplayName());
+    }
+
+    @Override
+    public String getDisplayName() {
+        final Conversation c = getConversation();
+        final String name = getBookmarkName();
+        if (c != null) {
+            return c.getName().toString();
+        } else if (printableValue(name, false)) {
+            return name.trim();
+        } else {
+            Jid jid = this.getJid();
+            return jid != null && jid.getLocal() != null ? jid.getLocal() : "";
+        }
+    }
+
+    public static boolean printableValue(@Nullable String value, boolean permitNone) {
+        return value != null && !value.trim().isEmpty() && (permitNone || !"None".equals(value));
+    }
+
+    public static boolean printableValue(@Nullable String value) {
+        return printableValue(value, true);
+    }
+
+    @Override
+    public Jid getJid() {
+        return this.jid;
+    }
+
+    public Jid getFullJid() {
+        final String nick = Strings.nullToEmpty(getNick()).trim();
+        if (jid == null || nick.isEmpty()) {
+            return jid;
+        }
+        try {
+            return jid.withResource(nick);
+        } catch (final IllegalArgumentException e) {
+            return jid;
+        }
+    }
+
+    @Override
+    public List<Tag> getTags(final Context context) {
+        final ImmutableList.Builder<Tag> tags = new ImmutableList.Builder<>();
+        for (final Element element : getChildren()) {
+            final String content = element.getContent();
+            if (Strings.isNullOrEmpty(content)) {
+                continue;
+            }
+            if (element.getName().equals("group")) {
+                tags.add(new Tag(content));
+            }
+        }
+        return tags.build();
+    }
+
+    public String getNick() {
+        return Strings.emptyToNull(this.findChildContent("nick"));
+    }
+
+    public void setNick(String nick) {
+        Element element = this.findChild("nick");
+        if (element == null) {
+            element = this.addChild("nick");
+        }
+        element.setContent(nick);
+    }
+
+    public boolean autojoin() {
+        return this.getAttributeAsBoolean("autojoin");
+    }
+
+    public String getPassword() {
+        return this.findChildContent("password");
+    }
+
+    public void setPassword(String password) {
+        Element element = this.findChild("password");
+        if (element != null) {
+            element.setContent(password);
+        }
+    }
+
+    @Override
+    public boolean match(Context context, String needle) {
+        if (needle == null) {
+            return true;
+        }
+        needle = needle.toLowerCase(Locale.US);
+        final Jid jid = getJid();
+        return (jid != null && jid.toString().contains(needle))
+                || getDisplayName().toLowerCase(Locale.US).contains(needle)
+                || matchInTag(context, needle);
+    }
+
+    private boolean matchInTag(Context context, String needle) {
+        needle = needle.toLowerCase(Locale.US);
+        for (Tag tag : getTags(context)) {
+            if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Account getAccount() {
+        return this.account;
+    }
+
+    public synchronized Conversation getConversation() {
+        return this.conversation != null ? this.conversation.get() : null;
+    }
+
+    public synchronized void setConversation(Conversation conversation) {
+        if (this.conversation != null) {
+            this.conversation.clear();
+        }
+        if (conversation == null) {
+            this.conversation = null;
+        } else {
+            this.conversation = new WeakReference<>(conversation);
+        }
+    }
+
+    public String getBookmarkName() {
+        return this.getAttribute("name");
+    }
+
+    public boolean setBookmarkName(String name) {
+        String before = getBookmarkName();
+        if (name != null) {
+            this.setAttribute("name", name);
+        } else {
+            this.removeAttribute("name");
+        }
+        return StringUtils.changed(before, name);
+    }
+
+    @Override
+    public int getAvatarBackgroundColor() {
+        return UIHelper.getColorForName(
+                jid != null ? jid.asBareJid().toString() : getDisplayName());
+    }
+
+    @Override
+    public String getAvatarName() {
+        return getDisplayName();
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 6437cffa3..b15381e58 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -5,22 +5,8 @@ import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.text.TextUtils;
-
 import androidx.annotation.NonNull;
-
 import com.google.common.base.Strings;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.android.AbstractPhoneContact;
 import eu.siacs.conversations.android.JabberIdContact;
@@ -31,6 +17,15 @@ import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.jingle.RtpCapability;
 import eu.siacs.conversations.xmpp.pep.Avatar;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
 
 public class Contact implements ListItem, Blockable {
     public static final String TABLENAME = "contacts";
@@ -69,10 +64,21 @@ public class Contact implements ListItem, Blockable {
     private String mLastPresence = null;
     private RtpCapability.Capability rtpCapability;
 
-    public Contact(final String account, final String systemName, final String serverName, final String presenceName,
-                   final Jid jid, final int subscription, final String photoUri,
-                   final Uri systemAccount, final String keys, final String avatar, final long lastseen,
-                   final String presence, final String groups, final RtpCapability.Capability rtpCapability) {
+    public Contact(
+            final String account,
+            final String systemName,
+            final String serverName,
+            final String presenceName,
+            final Jid jid,
+            final int subscription,
+            final String photoUri,
+            final Uri systemAccount,
+            final String keys,
+            final String avatar,
+            final long lastseen,
+            final String presence,
+            final String groups,
+            final RtpCapability.Capability rtpCapability) {
         this.accountUuid = account;
         this.systemName = systemName;
         this.serverName = serverName;
@@ -91,7 +97,7 @@ public class Contact implements ListItem, Blockable {
         if (avatar != null) {
             this.avatar = new Avatar();
             this.avatar.sha1sum = avatar;
-            this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
+            this.avatar.origin = Avatar.Origin.VCARD; // always assume worst
         }
         try {
             this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
@@ -122,7 +128,8 @@ public class Contact implements ListItem, Blockable {
         } catch (Exception e) {
             systemAccount = null;
         }
-        return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
+        return new Contact(
+                cursor.getString(cursor.getColumnIndex(ACCOUNT)),
                 cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
                 cursor.getString(cursor.getColumnIndex(SERVERNAME)),
                 cursor.getString(cursor.getColumnIndex(PRESENCE_NAME)),
@@ -135,7 +142,8 @@ public class Contact implements ListItem, Blockable {
                 cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
                 cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
                 cursor.getString(cursor.getColumnIndex(GROUPS)),
-                RtpCapability.Capability.of(cursor.getString(cursor.getColumnIndex(RTP_CAPABILITY))));
+                RtpCapability.Capability.of(
+                        cursor.getString(cursor.getColumnIndex(RTP_CAPABILITY))));
     }
 
     public String getDisplayName() {
@@ -151,12 +159,15 @@ public class Contact implements ListItem, Blockable {
             return this.systemName;
         } else if (!TextUtils.isEmpty(this.serverName)) {
             return this.serverName;
-        } else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && JidHelper.isQuicksyDomain(jid.getDomain())) || mutualPresenceSubscription())) {
+        } else if (!TextUtils.isEmpty(this.presenceName)
+                && ((QuickConversationsService.isQuicksy()
+                                && JidHelper.isQuicksyDomain(jid.getDomain()))
+                        || mutualPresenceSubscription())) {
             return this.presenceName;
         } else if (jid.getLocal() != null) {
             return JidHelper.localPartOrFallback(jid);
         } else {
-            return jid.getDomain().toEscapedString();
+            return jid.getDomain().toString();
         }
     }
 
@@ -166,7 +177,7 @@ public class Contact implements ListItem, Blockable {
         } else if (jid.getLocal() != null) {
             return JidHelper.localPartOrFallback(jid);
         } else {
-            return jid.getDomain().toEscapedString();
+            return jid.getDomain().toString();
         }
     }
 
@@ -201,9 +212,9 @@ public class Contact implements ListItem, Blockable {
             }
             return true;
         } else {
-            return jid.toString().contains(needle) ||
-                    getDisplayName().toLowerCase(Locale.US).contains(needle) ||
-                    matchInTag(context, needle);
+            return jid.toString().contains(needle)
+                    || getDisplayName().toLowerCase(Locale.US).contains(needle)
+                    || matchInTag(context, needle);
         }
     }
 
@@ -354,8 +365,8 @@ public class Contact implements ListItem, Blockable {
     }
 
     public boolean showInRoster() {
-        return (this.getOption(Contact.Options.IN_ROSTER) && (!this
-                .getOption(Contact.Options.DIRTY_DELETE)))
+        return (this.getOption(Contact.Options.IN_ROSTER)
+                        && (!this.getOption(Contact.Options.DIRTY_DELETE)))
                 || (this.getOption(Contact.Options.DIRTY_PUSH));
     }
 
@@ -430,12 +441,11 @@ public class Contact implements ListItem, Blockable {
 
     @Override
     public int compareTo(@NonNull final ListItem another) {
-        return this.getDisplayName().compareToIgnoreCase(
-                another.getDisplayName());
+        return this.getDisplayName().compareToIgnoreCase(another.getDisplayName());
     }
 
     public String getServer() {
-        return getJid().getDomain().toEscapedString();
+        return getJid().getDomain().toString();
     }
 
     public void setAvatar(Avatar avatar) {
@@ -446,7 +456,10 @@ public class Contact implements ListItem, Blockable {
         if (this.avatar != null && this.avatar.equals(avatar)) {
             return;
         }
-        if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
+        if (!previouslyOmittedPepFetch
+                && this.avatar != null
+                && this.avatar.origin == Avatar.Origin.PEP
+                && avatar.origin == Avatar.Origin.VCARD) {
             return;
         }
         this.avatar = avatar;
@@ -561,7 +574,8 @@ public class Contact implements ListItem, Blockable {
 
     @Override
     public int getAvatarBackgroundColor() {
-        return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
+        return UIHelper.getColorForName(
+                jid != null ? jid.asBareJid().toString() : getDisplayName());
     }
 
     @Override
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 1f192d57a..6394ee1a4 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -137,8 +137,7 @@ public class Conversation extends AbstractEntity
                 cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
                 cursor.getString(cursor.getColumnIndexOrThrow(CONTACT)),
                 cursor.getString(cursor.getColumnIndexOrThrow(ACCOUNT)),
-                JidHelper.parseOrFallbackToInvalid(
-                        cursor.getString(cursor.getColumnIndexOrThrow(CONTACTJID))),
+                Jid.ofOrInvalid(cursor.getString(cursor.getColumnIndexOrThrow(CONTACTJID))),
                 cursor.getLong(cursor.getColumnIndexOrThrow(CREATED)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(MODE)),
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 5129d0bfd..a1edc2a03 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -1,14 +1,10 @@
 package eu.siacs.conversations.entities;
 
-import android.text.TextUtils;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.services.AvatarService;
@@ -21,7 +17,6 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
 import eu.siacs.conversations.xmpp.forms.Data;
 import eu.siacs.conversations.xmpp.forms.Field;
 import eu.siacs.conversations.xmpp.pep.Avatar;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -68,7 +63,8 @@ public class MucOptions {
     public boolean setSelf(User user) {
         this.self = user;
         final boolean roleChanged = this.conversation.setAttribute("role", user.role.toString());
-        final boolean affiliationChanged = this.conversation.setAttribute("affiliation", user.affiliation.toString());
+        final boolean affiliationChanged =
+                this.conversation.setAttribute("affiliation", user.affiliation.toString());
         return roleChanged || affiliationChanged;
     }
 
@@ -123,21 +119,30 @@ public class MucOptions {
             final var identities = serviceDiscoveryResult.getIdentities();
             final String identityName = !identities.isEmpty() ? identities.get(0).getName() : null;
             final Jid jid = conversation.getJid();
-            if (identityName != null && !identityName.equals(jid == null ? null : jid.getEscapedLocal())) {
+            if (identityName != null && !identityName.equals(jid == null ? null : jid.getLocal())) {
                 name = identityName;
             } else {
                 name = null;
             }
         }
         boolean changed = conversation.setAttribute("muc_name", name);
-        changed |= conversation.setAttribute(Conversation.ATTRIBUTE_MEMBERS_ONLY, this.hasFeature("muc_membersonly"));
-        changed |= conversation.setAttribute(Conversation.ATTRIBUTE_MODERATED, this.hasFeature("muc_moderated"));
-        changed |= conversation.setAttribute(Conversation.ATTRIBUTE_NON_ANONYMOUS, this.hasFeature("muc_nonanonymous"));
+        changed |=
+                conversation.setAttribute(
+                        Conversation.ATTRIBUTE_MEMBERS_ONLY, this.hasFeature("muc_membersonly"));
+        changed |=
+                conversation.setAttribute(
+                        Conversation.ATTRIBUTE_MODERATED, this.hasFeature("muc_moderated"));
+        changed |=
+                conversation.setAttribute(
+                        Conversation.ATTRIBUTE_NON_ANONYMOUS, this.hasFeature("muc_nonanonymous"));
         return changed;
     }
 
     private Data getRoomInfoForm() {
-        final List<Data> forms = serviceDiscoveryResult == null ? Collections.emptyList() : serviceDiscoveryResult.forms;
+        final List<Data> forms =
+                serviceDiscoveryResult == null
+                        ? Collections.emptyList()
+                        : serviceDiscoveryResult.forms;
         return forms.isEmpty() ? new Data() : forms.get(0);
     }
 
@@ -146,7 +151,8 @@ public class MucOptions {
     }
 
     public boolean hasFeature(String feature) {
-        return this.serviceDiscoveryResult != null && this.serviceDiscoveryResult.features.contains(feature);
+        return this.serviceDiscoveryResult != null
+                && this.serviceDiscoveryResult.features.contains(feature);
     }
 
     public boolean hasVCards() {
@@ -154,7 +160,8 @@ public class MucOptions {
     }
 
     public boolean canInvite() {
-        final boolean hasPermission = !membersOnly() || self.getRole().ranks(Role.MODERATOR) || allowInvites();
+        final boolean hasPermission =
+                !membersOnly() || self.getRole().ranks(Role.MODERATOR) || allowInvites();
         return hasPermission && online();
     }
 
@@ -177,7 +184,7 @@ public class MucOptions {
     public boolean allowPm() {
         final Field field = getRoomInfoForm().getFieldByName("muc#roomconfig_allowpm");
         if (field == null) {
-            return true; //fall back if field does not exists
+            return true; // fall back if field does not exists
         }
         if ("anyone".equals(field.getValue())) {
             return true;
@@ -192,7 +199,7 @@ public class MucOptions {
 
     public boolean allowPmRaw() {
         final Field field = getRoomInfoForm().getFieldByName("muc#roomconfig_allowpm");
-        return  field == null || Arrays.asList("anyone","participants").contains(field.getValue());
+        return field == null || Arrays.asList("anyone", "participants").contains(field.getValue());
     }
 
     public boolean participating() {
@@ -204,7 +211,9 @@ public class MucOptions {
     }
 
     public List<String> getFeatures() {
-        return this.serviceDiscoveryResult != null ? this.serviceDiscoveryResult.features : Collections.emptyList();
+        return this.serviceDiscoveryResult != null
+                ? this.serviceDiscoveryResult.features
+                : Collections.emptyList();
     }
 
     public boolean nonanonymous() {
@@ -240,7 +249,8 @@ public class MucOptions {
                         break;
                     }
                 }
-                boolean self = user.realJid != null && user.realJid.equals(account.getJid().asBareJid());
+                boolean self =
+                        user.realJid != null && user.realJid.equals(account.getJid().asBareJid());
                 if (membersOnly()
                         && nonanonymous()
                         && user.affiliation.ranks(Affiliation.MEMBER)
@@ -257,7 +267,7 @@ public class MucOptions {
         return user;
     }
 
-    //returns true if real jid was new;
+    // returns true if real jid was new;
     public boolean updateUser(User user) {
         User old;
         boolean realJidFound = false;
@@ -266,7 +276,7 @@ public class MucOptions {
             realJidFound = old != null;
             if (old != null) {
                 if (old.fullJid != null) {
-                    return false; //don't add. user already exists
+                    return false; // don't add. user already exists
                 } else {
                     synchronized (users) {
                         users.remove(old);
@@ -288,7 +298,10 @@ public class MucOptions {
             if (old != null) {
                 users.remove(old);
             }
-            boolean fullJidIsSelf = isOnline && user.getFullJid() != null && user.getFullJid().equals(self.getFullJid());
+            boolean fullJidIsSelf =
+                    isOnline
+                            && user.getFullJid() != null
+                            && user.getFullJid().equals(self.getFullJid());
             if ((!membersOnly() || user.getAffiliation().ranks(Affiliation.MEMBER))
                     && user.getAffiliation().outranks(Affiliation.OUTCAST)
                     && !fullJidIsSelf) {
@@ -329,7 +342,9 @@ public class MucOptions {
 
     public User findUserByOccupantId(final String occupantId) {
         synchronized (this.users) {
-            return Strings.isNullOrEmpty(occupantId) ? null : Iterables.find(this.users, u -> occupantId.equals(u.occupantId),null);
+            return Strings.isNullOrEmpty(occupantId)
+                    ? null
+                    : Iterables.find(this.users, u -> occupantId.equals(u.occupantId), null);
         }
     }
 
@@ -345,7 +360,8 @@ public class MucOptions {
 
     public User findUser(ReadByMarker readByMarker) {
         if (readByMarker.getRealJid() != null) {
-            return findOrCreateUserByRealJid(readByMarker.getRealJid().asBareJid(), readByMarker.getFullJid());
+            return findOrCreateUserByRealJid(
+                    readByMarker.getRealJid().asBareJid(), readByMarker.getFullJid());
         } else if (readByMarker.getFullJid() != null) {
             return findUserByFullJid(readByMarker.getFullJid());
         } else {
@@ -361,7 +377,7 @@ public class MucOptions {
         if (existing != null) {
             return existing;
         } else if (reaction.from != null) {
-            return new User(this,reaction.from);
+            return new User(this, reaction.from);
         } else {
             return null;
         }
@@ -369,7 +385,7 @@ public class MucOptions {
 
     public List<User> findUsers(final Collection<Reaction> reactions) {
         final ImmutableList.Builder<User> builder = new ImmutableList.Builder<>();
-        for(final Reaction reaction : reactions) {
+        for (final Reaction reaction : reactions) {
             final var user = findUser(reaction);
             if (user != null) {
                 builder.add(user);
@@ -400,7 +416,8 @@ public class MucOptions {
         synchronized (users) {
             ArrayList<User> users = new ArrayList<>();
             for (User user : this.users) {
-                if (!user.isDomain() && (includeOffline || user.getRole().ranks(Role.PARTICIPANT))) {
+                if (!user.isDomain()
+                        && (includeOffline || user.getRole().ranks(Role.PARTICIPANT))) {
                     users.add(user);
                 }
             }
@@ -429,7 +446,8 @@ public class MucOptions {
         jids.add(account.getJid().asBareJid());
         synchronized (users) {
             for (User user : users) {
-                if (user.getRealJid() == null || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
+                if (user.getRealJid() == null
+                        || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
                     subset.add(user);
                 }
                 if (subset.size() >= max) {
@@ -445,7 +463,8 @@ public class MucOptions {
         HashSet<Jid> jids = new HashSet<>();
         for (User user : users) {
             jids.add(user.getAccount().getJid().asBareJid());
-            if (user.getRealJid() == null || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
+            if (user.getRealJid() == null
+                    || (user.getRealJid().getLocal() != null && jids.add(user.getRealJid()))) {
                 subset.add(user);
             }
             if (subset.size() >= max) {
@@ -477,7 +496,8 @@ public class MucOptions {
 
     public String getProposedNickPure() {
         final Bookmark bookmark = this.conversation.getBookmark();
-        final String bookmarkedNick = normalize(account.getJid(), bookmark == null ? null : bookmark.getNick());
+        final String bookmarkedNick =
+                normalize(account.getJid(), bookmark == null ? null : bookmark.getNick());
         if (bookmarkedNick != null) {
             return bookmarkedNick;
         } else {
@@ -503,7 +523,6 @@ public class MucOptions {
         } catch (final IllegalArgumentException e) {
             return null;
         }
-
     }
 
     public String getActualNick() {
@@ -649,7 +668,8 @@ public class MucOptions {
 
     public String getPassword() {
         this.password = conversation.getAttribute(Conversation.ATTRIBUTE_MUC_PASSWORD);
-        if (this.password == null && conversation.getBookmark() != null
+        if (this.password == null
+                && conversation.getBookmark() != null
                 && conversation.getBookmark().getPassword() != null) {
             return conversation.getBookmark().getPassword();
         } else {
@@ -674,7 +694,12 @@ public class MucOptions {
         ArrayList<Jid> members = new ArrayList<>();
         synchronized (users) {
             for (User user : users) {
-                if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null && !user.realJid.asBareJid().equals(conversation.account.getJid().asBareJid()) && (!user.isDomain() || includeDomains)) {
+                if (user.affiliation.ranks(Affiliation.MEMBER)
+                        && user.realJid != null
+                        && !user.realJid
+                                .asBareJid()
+                                .equals(conversation.account.getJid().asBareJid())
+                        && (!user.isDomain() || includeDomains)) {
                     members.add(user.realJid);
                 }
             }
@@ -790,9 +815,7 @@ public class MucOptions {
         void onFailure();
     }
 
-    public interface OnRenameListener extends OnEventListener {
-
-    }
+    public interface OnRenameListener extends OnEventListener {}
 
     public static class User implements Comparable<User>, AvatarService.Avatarable {
         private Role role = Role.NONE;
@@ -867,7 +890,10 @@ public class MucOptions {
             if (avatar != null) {
                 return avatar.getFilename();
             }
-            Avatar avatar = realJid != null ? getAccount().getRoster().getContact(realJid).getAvatar() : null;
+            Avatar avatar =
+                    realJid != null
+                            ? getAccount().getRoster().getContact(realJid).getAvatar()
+                            : null;
             return avatar == null ? null : avatar.getFilename();
         }
 
@@ -895,7 +921,6 @@ public class MucOptions {
             if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
                 return false;
             return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
-
         }
 
         public boolean isDomain() {
@@ -913,7 +938,13 @@ public class MucOptions {
 
         @Override
         public String toString() {
-            return "[fulljid:" + fullJid + ",realjid:" + realJid + ",affiliation" + affiliation.toString() + "]";
+            return "[fulljid:"
+                    + fullJid
+                    + ",realjid:"
+                    + realJid
+                    + ",affiliation"
+                    + affiliation.toString()
+                    + "]";
         }
 
         public boolean realJidMatchesAccount() {
diff --git a/src/main/java/eu/siacs/conversations/entities/RawBlockable.java b/src/main/java/eu/siacs/conversations/entities/RawBlockable.java
index 664a6a1c9..97f63d99c 100644
--- a/src/main/java/eu/siacs/conversations/entities/RawBlockable.java
+++ b/src/main/java/eu/siacs/conversations/entities/RawBlockable.java
@@ -2,14 +2,12 @@ package eu.siacs.conversations.entities;
 
 import android.content.Context;
 import android.text.TextUtils;
-
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xmpp.Jid;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
-import eu.siacs.conversations.utils.UIHelper;
-import eu.siacs.conversations.xmpp.Jid;
-
 public class RawBlockable implements ListItem, Blockable {
 
     private final Account account;
@@ -40,7 +38,7 @@ public class RawBlockable implements ListItem, Blockable {
         if (jid.isFullJid()) {
             return jid.getResource();
         } else {
-            return jid.toEscapedString();
+            return jid.toString();
         }
     }
 
@@ -62,7 +60,7 @@ public class RawBlockable implements ListItem, Blockable {
         needle = needle.toLowerCase(Locale.US).trim();
         String[] parts = needle.split("\\s+");
         for (String part : parts) {
-            if (!jid.toEscapedString().contains(part)) {
+            if (!jid.toString().contains(part)) {
                 return false;
             }
         }
@@ -76,7 +74,7 @@ public class RawBlockable implements ListItem, Blockable {
 
     @Override
     public int getAvatarBackgroundColor() {
-        return  UIHelper.getColorForName(jid.toEscapedString());
+        return UIHelper.getColorForName(jid.toString());
     }
 
     @Override
@@ -86,7 +84,6 @@ public class RawBlockable implements ListItem, Blockable {
 
     @Override
     public int compareTo(ListItem o) {
-        return this.getDisplayName().compareToIgnoreCase(
-				o.getDisplayName());
+        return this.getDisplayName().compareToIgnoreCase(o.getDisplayName());
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/eu/siacs/conversations/entities/Reaction.java b/src/main/java/eu/siacs/conversations/entities/Reaction.java
index 3a945675f..cd860cd1f 100644
--- a/src/main/java/eu/siacs/conversations/entities/Reaction.java
+++ b/src/main/java/eu/siacs/conversations/entities/Reaction.java
@@ -1,9 +1,7 @@
 package eu.siacs.conversations.entities;
 
 import android.util.Log;
-
 import androidx.annotation.NonNull;
-
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.Collections2;
@@ -20,11 +18,9 @@ import com.google.gson.reflect.TypeToken;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
 import com.google.gson.stream.JsonWriter;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.utils.Emoticons;
 import eu.siacs.conversations.xmpp.Jid;
-
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
@@ -137,7 +133,7 @@ public class Reaction {
             if (value == null) {
                 out.nullValue();
             } else {
-                out.value(value.toEscapedString());
+                out.value(value.toString());
             }
         }
 
@@ -148,7 +144,7 @@ public class Reaction {
                 return null;
             } else if (in.peek() == JsonToken.STRING) {
                 final String value = in.nextString();
-                return Jid.ofEscaped(value);
+                return Jid.of(value);
             }
             throw new IOException("Unexpected token");
         }
diff --git a/src/main/java/eu/siacs/conversations/entities/Room.java b/src/main/java/eu/siacs/conversations/entities/Room.java
index 9e1d61fc3..c702c3189 100644
--- a/src/main/java/eu/siacs/conversations/entities/Room.java
+++ b/src/main/java/eu/siacs/conversations/entities/Room.java
@@ -3,7 +3,6 @@ package eu.siacs.conversations.entities;
 import com.google.common.base.Objects;
 import com.google.common.base.Strings;
 import com.google.common.collect.ComparisonChain;
-
 import eu.siacs.conversations.services.AvatarService;
 import eu.siacs.conversations.utils.LanguageUtils;
 import eu.siacs.conversations.utils.UIHelper;
@@ -25,9 +24,7 @@ public class Room implements AvatarService.Avatarable, Comparable<Room> {
         this.nusers = nusers;
     }
 
-    public Room() {
-
-    }
+    public Room() {}
 
     public String getName() {
         return name;
@@ -52,7 +49,7 @@ public class Room implements AvatarService.Avatarable, Comparable<Room> {
     @Override
     public int getAvatarBackgroundColor() {
         Jid room = getRoom();
-        return UIHelper.getColorForName(room != null ? room.asBareJid().toEscapedString() : name);
+        return UIHelper.getColorForName(room != null ? room.asBareJid().toString() : name);
     }
 
     @Override
@@ -65,9 +62,9 @@ public class Room implements AvatarService.Avatarable, Comparable<Room> {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         Room room = (Room) o;
-        return Objects.equal(address, room.address) &&
-                Objects.equal(name, room.name) &&
-                Objects.equal(description, room.description);
+        return Objects.equal(address, room.address)
+                && Objects.equal(name, room.name)
+                && Objects.equal(description, room.description);
     }
 
     @Override
@@ -75,7 +72,6 @@ public class Room implements AvatarService.Avatarable, Comparable<Room> {
         return Objects.hashCode(address, name, description);
     }
 
-
     public boolean contains(String needle) {
         return Strings.nullToEmpty(name).contains(needle)
                 || Strings.nullToEmpty(description).contains(needle)
@@ -90,4 +86,4 @@ public class Room implements AvatarService.Avatarable, Comparable<Room> {
                 .compare(Strings.nullToEmpty(address), Strings.nullToEmpty(o.address))
                 .result();
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 85e3a0d7c..9a4dad2a5 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -1,25 +1,8 @@
 package eu.siacs.conversations.generator;
 
-
 import android.os.Bundle;
 import android.util.Base64;
 import android.util.Log;
-
-import org.whispersystems.libsignal.IdentityKey;
-import org.whispersystems.libsignal.ecc.ECPublicKey;
-import org.whispersystems.libsignal.state.PreKeyRecord;
-import org.whispersystems.libsignal.state.SignedPreKeyRecord;
-
-import java.nio.ByteBuffer;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.UUID;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -35,6 +18,19 @@ import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.forms.Data;
 import eu.siacs.conversations.xmpp.pep.Avatar;
 import im.conversations.android.xmpp.model.stanza.Iq;
+import java.nio.ByteBuffer;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+import org.whispersystems.libsignal.IdentityKey;
+import org.whispersystems.libsignal.ecc.ECPublicKey;
+import org.whispersystems.libsignal.state.PreKeyRecord;
+import org.whispersystems.libsignal.state.SignedPreKeyRecord;
 
 public class IqGenerator extends AbstractGenerator {
 
@@ -152,7 +148,7 @@ public class IqGenerator extends AbstractGenerator {
         final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB);
         final Element retract = pubsub.addChild("retract");
         retract.setAttribute("node", node);
-        retract.setAttribute("notify","true");
+        retract.setAttribute("notify", "true");
         retract.addChild("item").setAttribute("id", id);
         return packet;
     }
@@ -165,7 +161,8 @@ public class IqGenerator extends AbstractGenerator {
         return publish(Namespace.AVATAR_DATA, item, options);
     }
 
-    public Iq publishElement(final String namespace, final Element element, String id, final Bundle options) {
+    public Iq publishElement(
+            final String namespace, final Element element, String id, final Bundle options) {
         final Element item = new Element("item");
         item.setAttribute("id", id);
         item.addChild(element);
@@ -175,8 +172,7 @@ public class IqGenerator extends AbstractGenerator {
     public Iq publishAvatarMetadata(final Avatar avatar, final Bundle options) {
         final Element item = new Element("item");
         item.setAttribute("id", avatar.sha1sum);
-        final Element metadata = item
-                .addChild("metadata", Namespace.AVATAR_METADATA);
+        final Element metadata = item.addChild("metadata", Namespace.AVATAR_METADATA);
         final Element info = metadata.addChild("info");
         info.setAttribute("bytes", avatar.size);
         info.setAttribute("id", avatar.sha1sum);
@@ -263,7 +259,7 @@ public class IqGenerator extends AbstractGenerator {
         if (password != null) {
             conference.addChild("password").setContent(password);
         }
-        conference.setAttribute("autojoin",String.valueOf(autojoin));
+        conference.setAttribute("autojoin", String.valueOf(autojoin));
         conference.addChild(bookmark.getExtensions());
         return conference;
     }
@@ -286,8 +282,12 @@ public class IqGenerator extends AbstractGenerator {
         return displayed;
     }
 
-    public Iq publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
-                                   final Set<PreKeyRecord> preKeyRecords, final int deviceId, Bundle publishOptions) {
+    public Iq publishBundles(
+            final SignedPreKeyRecord signedPreKeyRecord,
+            final IdentityKey identityKey,
+            final Set<PreKeyRecord> preKeyRecords,
+            final int deviceId,
+            Bundle publishOptions) {
         final Element item = new Element("item");
         item.setAttribute("id", "current");
         final Element bundle = item.addChild("bundle", AxolotlService.PEP_PREFIX);
@@ -296,21 +296,26 @@ public class IqGenerator extends AbstractGenerator {
         ECPublicKey publicKey = signedPreKeyRecord.getKeyPair().getPublicKey();
         signedPreKeyPublic.setContent(Base64.encodeToString(publicKey.serialize(), Base64.NO_WRAP));
         final Element signedPreKeySignature = bundle.addChild("signedPreKeySignature");
-        signedPreKeySignature.setContent(Base64.encodeToString(signedPreKeyRecord.getSignature(), Base64.NO_WRAP));
+        signedPreKeySignature.setContent(
+                Base64.encodeToString(signedPreKeyRecord.getSignature(), Base64.NO_WRAP));
         final Element identityKeyElement = bundle.addChild("identityKey");
-        identityKeyElement.setContent(Base64.encodeToString(identityKey.serialize(), Base64.NO_WRAP));
+        identityKeyElement.setContent(
+                Base64.encodeToString(identityKey.serialize(), Base64.NO_WRAP));
 
         final Element prekeys = bundle.addChild("prekeys", AxolotlService.PEP_PREFIX);
         for (PreKeyRecord preKeyRecord : preKeyRecords) {
             final Element prekey = prekeys.addChild("preKeyPublic");
             prekey.setAttribute("preKeyId", preKeyRecord.getId());
-            prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.NO_WRAP));
+            prekey.setContent(
+                    Base64.encodeToString(
+                            preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.NO_WRAP));
         }
 
         return publish(AxolotlService.PEP_BUNDLES + ":" + deviceId, item, publishOptions);
     }
 
-    public Iq publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
+    public Iq publishVerification(
+            byte[] signature, X509Certificate[] certificates, final int deviceId) {
         final Element item = new Element("item");
         item.setAttribute("id", "current");
         final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
@@ -318,13 +323,16 @@ public class IqGenerator extends AbstractGenerator {
         for (int i = 0; i < certificates.length; ++i) {
             try {
                 Element certificate = chain.addChild("certificate");
-                certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.NO_WRAP));
+                certificate.setContent(
+                        Base64.encodeToString(certificates[i].getEncoded(), Base64.NO_WRAP));
                 certificate.setAttribute("index", i);
             } catch (CertificateEncodingException e) {
                 Log.d(Config.LOGTAG, "could not encode certificate");
             }
         }
-        verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.NO_WRAP));
+        verification
+                .addChild("signature")
+                .setContent(Base64.encodeToString(signature, Base64.NO_WRAP));
         return publish(AxolotlService.PEP_VERIFICATION + ":" + deviceId, item);
     }
 
@@ -337,7 +345,7 @@ public class IqGenerator extends AbstractGenerator {
         if (mam.muc()) {
             packet.setTo(mam.getWith());
         } else if (mam.getWith() != null) {
-            data.put("with", mam.getWith().toEscapedString());
+            data.put("with", mam.getWith().toString());
         }
         final long start = mam.getStart();
         final long end = mam.getEnd();
@@ -366,7 +374,8 @@ public class IqGenerator extends AbstractGenerator {
         return iq;
     }
 
-    public Iq generateSetBlockRequest(final Jid jid, final boolean reportSpam, final String serverMsgId) {
+    public Iq generateSetBlockRequest(
+            final Jid jid, final boolean reportSpam, final String serverMsgId) {
         final Iq iq = new Iq(Iq.Type.SET);
         final Element block = iq.addChild("block", Namespace.BLOCKING);
         final Element item = block.addChild("item").setAttribute("jid", jid);
@@ -457,7 +466,9 @@ public class IqGenerator extends AbstractGenerator {
                 ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
                 bb.putLong(uuid.getMostSignificantBits());
                 bb.putLong(uuid.getLeastSignificantBits());
-                return Base64.encodeToString(bb.array(), Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP) + name.substring(pos);
+                return Base64.encodeToString(
+                                bb.array(), Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP)
+                        + name.substring(pos);
             } catch (Exception e) {
                 return name;
             }
@@ -466,7 +477,8 @@ public class IqGenerator extends AbstractGenerator {
         }
     }
 
-    public static Iq generateCreateAccountWithCaptcha(final Account account, final String id, final Data data) {
+    public static Iq generateCreateAccountWithCaptcha(
+            final Account account, final String id, final Data data) {
         final Iq register = new Iq(Iq.Type.SET);
         register.setFrom(account.getJid().asBareJid());
         register.setTo(account.getDomain());
@@ -492,7 +504,7 @@ public class IqGenerator extends AbstractGenerator {
         data.put("token", token);
         data.put("android-id", deviceId);
         if (muc != null) {
-            data.put("muc", muc.toEscapedString());
+            data.put("muc", muc.toString());
         }
         data.submit();
         command.addChild(data);
@@ -539,7 +551,9 @@ public class IqGenerator extends AbstractGenerator {
     public Iq queryAffiliation(Conversation conversation, String affiliation) {
         final Iq packet = new Iq(Iq.Type.GET);
         packet.setTo(conversation.getJid().asBareJid());
-        packet.query("http://jabber.org/protocol/muc#admin").addChild("item").setAttribute("affiliation", affiliation);
+        packet.query("http://jabber.org/protocol/muc#admin")
+                .addChild("item")
+                .setAttribute("affiliation", affiliation);
         return packet;
     }
 
@@ -551,9 +565,9 @@ public class IqGenerator extends AbstractGenerator {
         options.putString("muc#roomconfig_whois", "anyone");
         options.putString("muc#roomconfig_changesubject", "0");
         options.putString("muc#roomconfig_allowinvites", "0");
-        options.putString("muc#roomconfig_enablearchiving", "1"); //prosody
-        options.putString("mam", "1"); //ejabberd community
-        options.putString("muc#roomconfig_mam", "1"); //ejabberd saas
+        options.putString("muc#roomconfig_enablearchiving", "1"); // prosody
+        options.putString("mam", "1"); // ejabberd community
+        options.putString("muc#roomconfig_mam", "1"); // ejabberd saas
         return options;
     }
 
@@ -564,9 +578,9 @@ public class IqGenerator extends AbstractGenerator {
         options.putString("muc#roomconfig_publicroom", "1");
         options.putString("muc#roomconfig_whois", "moderators");
         options.putString("muc#roomconfig_changesubject", "0");
-        options.putString("muc#roomconfig_enablearchiving", "1"); //prosody
-        options.putString("mam", "1"); //ejabberd community
-        options.putString("muc#roomconfig_mam", "1"); //ejabberd saas
+        options.putString("muc#roomconfig_enablearchiving", "1"); // prosody
+        options.putString("mam", "1"); // ejabberd community
+        options.putString("muc#roomconfig_mam", "1"); // ejabberd saas
         return options;
     }
 
@@ -592,14 +606,14 @@ public class IqGenerator extends AbstractGenerator {
     public Iq queryDiscoItems(final Jid jid) {
         final Iq packet = new Iq(Iq.Type.GET);
         packet.setTo(jid);
-        packet.addChild("query",Namespace.DISCO_ITEMS);
+        packet.addChild("query", Namespace.DISCO_ITEMS);
         return packet;
     }
 
     public Iq queryDiscoInfo(final Jid jid) {
         final Iq packet = new Iq(Iq.Type.GET);
         packet.setTo(jid);
-        packet.addChild("query",Namespace.DISCO_INFO);
+        packet.addChild("query", Namespace.DISCO_INFO);
         return packet;
     }
 }
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index ac42857fd..8f5178d8e 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -1,93 +1,93 @@
 package eu.siacs.conversations.parser;
 
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.MucOptions;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import im.conversations.android.xmpp.model.stanza.Stanza;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
 
 public abstract class AbstractParser {
 
-	protected final XmppConnectionService mXmppConnectionService;
-	protected final Account account;
-
-	protected AbstractParser(final XmppConnectionService service, final Account account) {
-		this.mXmppConnectionService = service;
-		this.account = account;
-	}
-
-	public static Long parseTimestamp(Element element, Long d) {
-		return parseTimestamp(element,d,false);
-	}
-
-	public static Long parseTimestamp(Element element, Long d, boolean ignoreCsiAndSm) {
-		long min = Long.MAX_VALUE;
-		boolean returnDefault = true;
-		final Jid to;
-		if (ignoreCsiAndSm && element instanceof Stanza stanza) {
-			to = stanza.getTo();
-		} else {
-			to = null;
-		}
-		for(Element child : element.getChildren()) {
-			if ("delay".equals(child.getName()) && "urn:xmpp:delay".equals(child.getNamespace())) {
-				final Jid f = to == null ? null : InvalidJid.getNullForInvalid(child.getAttributeAsJid("from"));
-				if (f != null && (to.asBareJid().equals(f) || to.getDomain().equals(f))) {
-					continue;
-				}
-				final String stamp = child.getAttribute("stamp");
-				if (stamp != null) {
-					try {
-						min = Math.min(min,AbstractParser.parseTimestamp(stamp));
-						returnDefault = false;
-					} catch (Throwable t) {
-						//ignore
-					}
-				}
-			}
-		}
-		if (returnDefault) {
-			return d;
-		} else {
-			return min;
-		}
-	}
-
-	public static long parseTimestamp(Element element) {
-		return parseTimestamp(element, System.currentTimeMillis());
-	}
-
-	public static long parseTimestamp(String timestamp) throws ParseException {
-		timestamp = timestamp.replace("Z", "+0000");
-		SimpleDateFormat dateFormat;
-		long ms;
-		if (timestamp.length() >= 25 && timestamp.charAt(19) == '.') {
-			String millis = timestamp.substring(19,timestamp.length() - 5);
-			try {
-				double fractions = Double.parseDouble("0" + millis);
-				ms = Math.round(1000 * fractions);
-			} catch (NumberFormatException e) {
-				ms = 0;
-			}
-		} else {
-			ms = 0;
-		}
-		timestamp = timestamp.substring(0,19)+timestamp.substring(timestamp.length() -5);
-		dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
-		return Math.min(dateFormat.parse(timestamp).getTime()+ms, System.currentTimeMillis());
-	}
+    protected final XmppConnectionService mXmppConnectionService;
+    protected final Account account;
+
+    protected AbstractParser(final XmppConnectionService service, final Account account) {
+        this.mXmppConnectionService = service;
+        this.account = account;
+    }
+
+    public static Long parseTimestamp(Element element, Long d) {
+        return parseTimestamp(element, d, false);
+    }
+
+    public static Long parseTimestamp(Element element, Long d, boolean ignoreCsiAndSm) {
+        long min = Long.MAX_VALUE;
+        boolean returnDefault = true;
+        final Jid to;
+        if (ignoreCsiAndSm && element instanceof Stanza stanza) {
+            to = stanza.getTo();
+        } else {
+            to = null;
+        }
+        for (Element child : element.getChildren()) {
+            if ("delay".equals(child.getName()) && "urn:xmpp:delay".equals(child.getNamespace())) {
+                final Jid f =
+                        to == null
+                                ? null
+                                : Jid.Invalid.getNullForInvalid(child.getAttributeAsJid("from"));
+                if (f != null && (to.asBareJid().equals(f) || to.getDomain().equals(f))) {
+                    continue;
+                }
+                final String stamp = child.getAttribute("stamp");
+                if (stamp != null) {
+                    try {
+                        min = Math.min(min, AbstractParser.parseTimestamp(stamp));
+                        returnDefault = false;
+                    } catch (Throwable t) {
+                        // ignore
+                    }
+                }
+            }
+        }
+        if (returnDefault) {
+            return d;
+        } else {
+            return min;
+        }
+    }
+
+    public static long parseTimestamp(Element element) {
+        return parseTimestamp(element, System.currentTimeMillis());
+    }
+
+    public static long parseTimestamp(String timestamp) throws ParseException {
+        timestamp = timestamp.replace("Z", "+0000");
+        SimpleDateFormat dateFormat;
+        long ms;
+        if (timestamp.length() >= 25 && timestamp.charAt(19) == '.') {
+            String millis = timestamp.substring(19, timestamp.length() - 5);
+            try {
+                double fractions = Double.parseDouble("0" + millis);
+                ms = Math.round(1000 * fractions);
+            } catch (NumberFormatException e) {
+                ms = 0;
+            }
+        } else {
+            ms = 0;
+        }
+        timestamp = timestamp.substring(0, 19) + timestamp.substring(timestamp.length() - 5);
+        dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
+        return Math.min(dateFormat.parse(timestamp).getTime() + ms, System.currentTimeMillis());
+    }
 
     public static long getTimestamp(final String input) throws ParseException {
         if (input == null) {
@@ -120,93 +120,94 @@ public abstract class AbstractParser {
         }
     }
 
-	protected void updateLastseen(final Account account, final Jid from) {
-		final Contact contact = account.getRoster().getContact(from);
-		contact.setLastResource(from.isBareJid() ? "" : from.getResource());
-	}
-
-	protected static String avatarData(Element items) {
-		Element item = items.findChild("item");
-		if (item == null) {
-			return null;
-		}
-		return item.findChildContent("data", "urn:xmpp:avatar:data");
-	}
-
-	public static MucOptions.User parseItem(Conversation conference, Element item) {
-		return parseItem(conference,item, null);
-	}
-
-	public static MucOptions.User parseItem(final Conversation conference, Element item, Jid fullJid) {
-		final String local = conference.getJid().getLocal();
-		final String domain = conference.getJid().getDomain().toEscapedString();
-		String affiliation = item.getAttribute("affiliation");
-		String role = item.getAttribute("role");
-		String nick = item.getAttribute("nick");
-		if (nick != null && fullJid == null) {
-			try {
-				fullJid = Jid.of(local, domain, nick);
-			} catch (IllegalArgumentException e) {
-				fullJid = null;
-			}
-		}
-		final Jid realJid = item.getAttributeAsJid("jid");
-		MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid);
-		if (InvalidJid.isValid(realJid)) {
-			user.setRealJid(realJid);
-		}
-		user.setAffiliation(affiliation);
-		user.setRole(role);
-		return user;
-	}
-
-	public static String extractErrorMessage(final Element packet) {
-		final Element error = packet.findChild("error");
-		if (error != null && error.getChildren().size() > 0) {
-			final List<String> errorNames = orderedElementNames(error.getChildren());
-			final String text = error.findChildContent("text");
-			if (text != null && !text.trim().isEmpty()) {
-				return prefixError(errorNames)+text;
-			} else if (errorNames.size() > 0){
-				return prefixError(errorNames)+errorNames.get(0).replace("-"," ");
-			}
-		}
-		return null;
-	}
-
-	public static String errorMessage(Element packet) {
-		final Element error = packet.findChild("error");
-		if (error != null && error.getChildren().size() > 0) {
-			final List<String> errorNames = orderedElementNames(error.getChildren());
-			final String text = error.findChildContent("text");
-			if (text != null && !text.trim().isEmpty()) {
-				return text;
-			} else if (errorNames.size() > 0){
-				return errorNames.get(0).replace("-"," ");
-			}
-		}
-		return null;
-	}
-
-	private static String prefixError(List<String> errorNames) {
-		if (errorNames.size() > 0) {
-			return errorNames.get(0)+'\u001f';
-		}
-		return "";
-	}
-
-	private static List<String> orderedElementNames(List<Element> children) {
-		List<String> names = new ArrayList<>();
-		for(Element child : children) {
-			final String name = child.getName();
-			if (name != null && !name.equals("text")) {
-				if ("urn:ietf:params:xml:ns:xmpp-stanzas".equals(child.getNamespace())) {
-					names.add(name);
-				} else {
-					names.add(0, name);
-				}
-			}
-		}
-		return names;
-	}
+    protected void updateLastseen(final Account account, final Jid from) {
+        final Contact contact = account.getRoster().getContact(from);
+        contact.setLastResource(from.isBareJid() ? "" : from.getResource());
+    }
+
+    protected static String avatarData(Element items) {
+        Element item = items.findChild("item");
+        if (item == null) {
+            return null;
+        }
+        return item.findChildContent("data", "urn:xmpp:avatar:data");
+    }
+
+    public static MucOptions.User parseItem(Conversation conference, Element item) {
+        return parseItem(conference, item, null);
+    }
+
+    public static MucOptions.User parseItem(
+            final Conversation conference, Element item, Jid fullJid) {
+        final String local = conference.getJid().getLocal();
+        final String domain = conference.getJid().getDomain().toString();
+        String affiliation = item.getAttribute("affiliation");
+        String role = item.getAttribute("role");
+        String nick = item.getAttribute("nick");
+        if (nick != null && fullJid == null) {
+            try {
+                fullJid = Jid.of(local, domain, nick);
+            } catch (IllegalArgumentException e) {
+                fullJid = null;
+            }
+        }
+        final Jid realJid = item.getAttributeAsJid("jid");
+        MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid);
+        if (Jid.Invalid.isValid(realJid)) {
+            user.setRealJid(realJid);
+        }
+        user.setAffiliation(affiliation);
+        user.setRole(role);
+        return user;
+    }
+
+    public static String extractErrorMessage(final Element packet) {
+        final Element error = packet.findChild("error");
+        if (error != null && error.getChildren().size() > 0) {
+            final List<String> errorNames = orderedElementNames(error.getChildren());
+            final String text = error.findChildContent("text");
+            if (text != null && !text.trim().isEmpty()) {
+                return prefixError(errorNames) + text;
+            } else if (errorNames.size() > 0) {
+                return prefixError(errorNames) + errorNames.get(0).replace("-", " ");
+            }
+        }
+        return null;
+    }
+
+    public static String errorMessage(Element packet) {
+        final Element error = packet.findChild("error");
+        if (error != null && error.getChildren().size() > 0) {
+            final List<String> errorNames = orderedElementNames(error.getChildren());
+            final String text = error.findChildContent("text");
+            if (text != null && !text.trim().isEmpty()) {
+                return text;
+            } else if (errorNames.size() > 0) {
+                return errorNames.get(0).replace("-", " ");
+            }
+        }
+        return null;
+    }
+
+    private static String prefixError(List<String> errorNames) {
+        if (errorNames.size() > 0) {
+            return errorNames.get(0) + '\u001f';
+        }
+        return "";
+    }
+
+    private static List<String> orderedElementNames(List<Element> children) {
+        List<String> names = new ArrayList<>();
+        for (Element child : children) {
+            final String name = child.getName();
+            if (name != null && !name.equals("text")) {
+                if ("urn:ietf:params:xml:ns:xmpp-stanzas".equals(child.getNamespace())) {
+                    names.add(name);
+                } else {
+                    names.add(0, name);
+                }
+            }
+        }
+        return names;
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index 339c025c9..14efffd53 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -3,31 +3,9 @@ package eu.siacs.conversations.parser;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
-
 import androidx.annotation.NonNull;
-
 import com.google.common.base.CharMatcher;
 import com.google.common.io.BaseEncoding;
-
-import org.whispersystems.libsignal.IdentityKey;
-import org.whispersystems.libsignal.InvalidKeyException;
-import org.whispersystems.libsignal.ecc.Curve;
-import org.whispersystems.libsignal.ecc.ECPublicKey;
-import org.whispersystems.libsignal.state.PreKeyBundle;
-
-import java.io.ByteArrayInputStream;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 import eu.siacs.conversations.entities.Account;
@@ -36,11 +14,27 @@ import eu.siacs.conversations.entities.Room;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.Namespace;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
 import eu.siacs.conversations.xmpp.forms.Data;
 import im.conversations.android.xmpp.model.stanza.Iq;
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.whispersystems.libsignal.IdentityKey;
+import org.whispersystems.libsignal.InvalidKeyException;
+import org.whispersystems.libsignal.ecc.Curve;
+import org.whispersystems.libsignal.ecc.ECPublicKey;
+import org.whispersystems.libsignal.state.PreKeyBundle;
 
 public class IqParser extends AbstractParser implements Consumer<Iq> {
 
@@ -94,8 +88,7 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 TextUtils.isEmpty(roomName) ? name : roomName,
                 description,
                 language,
-                nusers
-        );
+                nusers);
     }
 
     private void rosterItems(final Account account, final Element query) {
@@ -105,14 +98,16 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         }
         for (final Element item : query.getChildren()) {
             if (item.getName().equals("item")) {
-                final Jid jid = InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid"));
+                final Jid jid = Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("jid"));
                 if (jid == null) {
                     continue;
                 }
                 final String name = item.getAttribute("name");
                 final String subscription = item.getAttribute("subscription");
                 final Contact contact = account.getRoster().getContact(jid);
-                boolean bothPre = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+                boolean bothPre =
+                        contact.getOption(Contact.Options.TO)
+                                && contact.getOption(Contact.Options.FROM);
                 if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
                     contact.setServerName(name);
                     contact.parseGroupsFromElement(item);
@@ -126,9 +121,15 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                     contact.resetOption(Contact.Options.DIRTY_PUSH);
                     contact.parseSubscriptionFromElement(item);
                 }
-                boolean both = contact.getOption(Contact.Options.TO) && contact.getOption(Contact.Options.FROM);
+                boolean both =
+                        contact.getOption(Contact.Options.TO)
+                                && contact.getOption(Contact.Options.FROM);
                 if ((both != bothPre) && both) {
-                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": gained mutual presence subscription with " + contact.getJid());
+                    Log.d(
+                            Config.LOGTAG,
+                            account.getJid().asBareJid()
+                                    + ": gained mutual presence subscription with "
+                                    + contact.getJid());
                     AxolotlService axolotlService = account.getAxolotlService();
                     if (axolotlService != null) {
                         axolotlService.clearErrorsInFetchStatusMap(contact.getJid());
@@ -181,7 +182,15 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                         Integer id = Integer.valueOf(device.getAttribute("id"));
                         deviceIds.add(id);
                     } catch (NumberFormatException e) {
-                        Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered invalid <device> node in PEP (" + e.getMessage() + "):" + device.toString() + ", skipping...");
+                        Log.e(
+                                Config.LOGTAG,
+                                AxolotlService.LOGPREFIX
+                                        + " : "
+                                        + "Encountered invalid <device> node in PEP ("
+                                        + e.getMessage()
+                                        + "):"
+                                        + device.toString()
+                                        + ", skipping...");
                     }
                 }
             }
@@ -210,7 +219,12 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         try {
             publicKey = Curve.decodePoint(base64decode(signedPreKeyPublic), 0);
         } catch (final IllegalArgumentException | InvalidKeyException e) {
-            Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid signedPreKeyPublic in PEP: " + e.getMessage());
+            Log.e(
+                    Config.LOGTAG,
+                    AxolotlService.LOGPREFIX
+                            + " : "
+                            + "Invalid signedPreKeyPublic in PEP: "
+                            + e.getMessage());
         }
         return publicKey;
     }
@@ -223,7 +237,9 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         try {
             return base64decode(signedPreKeySignature);
         } catch (final IllegalArgumentException e) {
-            Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : Invalid base64 in signedPreKeySignature");
+            Log.e(
+                    Config.LOGTAG,
+                    AxolotlService.LOGPREFIX + " : Invalid base64 in signedPreKeySignature");
             return null;
         }
     }
@@ -236,7 +252,12 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         try {
             return new IdentityKey(base64decode(identityKey), 0);
         } catch (final IllegalArgumentException | InvalidKeyException e) {
-            Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid identityKey in PEP: " + e.getMessage());
+            Log.e(
+                    Config.LOGTAG,
+                    AxolotlService.LOGPREFIX
+                            + " : "
+                            + "Invalid identityKey in PEP: "
+                            + e.getMessage());
             return null;
         }
     }
@@ -245,7 +266,12 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
         Element item = getItem(packet);
         if (item == null) {
-            Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Couldn't find <item> in bundle IQ packet: " + packet);
+            Log.d(
+                    Config.LOGTAG,
+                    AxolotlService.LOGPREFIX
+                            + " : "
+                            + "Couldn't find <item> in bundle IQ packet: "
+                            + packet);
             return null;
         }
         final Element bundleElement = item.findChild("bundle");
@@ -254,12 +280,22 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         }
         final Element prekeysElement = bundleElement.findChild("prekeys");
         if (prekeysElement == null) {
-            Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Couldn't find <prekeys> in bundle IQ packet: " + packet);
+            Log.d(
+                    Config.LOGTAG,
+                    AxolotlService.LOGPREFIX
+                            + " : "
+                            + "Couldn't find <prekeys> in bundle IQ packet: "
+                            + packet);
             return null;
         }
         for (Element preKeyPublicElement : prekeysElement.getChildren()) {
             if (!preKeyPublicElement.getName().equals("preKeyPublic")) {
-                Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
+                Log.d(
+                        Config.LOGTAG,
+                        AxolotlService.LOGPREFIX
+                                + " : "
+                                + "Encountered unexpected tag in prekeys list: "
+                                + preKeyPublicElement);
                 continue;
             }
             final String preKey = preKeyPublicElement.getContent();
@@ -272,9 +308,22 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 final ECPublicKey preKeyPublic = Curve.decodePoint(base64decode(preKey), 0);
                 preKeyRecords.put(preKeyId, preKeyPublic);
             } catch (NumberFormatException e) {
-                Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "could not parse preKeyId from preKey " + preKeyPublicElement.toString());
+                Log.e(
+                        Config.LOGTAG,
+                        AxolotlService.LOGPREFIX
+                                + " : "
+                                + "could not parse preKeyId from preKey "
+                                + preKeyPublicElement.toString());
             } catch (Throwable e) {
-                Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid preKeyPublic (ID=" + preKeyId + ") in PEP: " + e.getMessage() + ", skipping...");
+                Log.e(
+                        Config.LOGTAG,
+                        AxolotlService.LOGPREFIX
+                                + " : "
+                                + "Invalid preKeyPublic (ID="
+                                + preKeyId
+                                + ") in PEP: "
+                                + e.getMessage()
+                                + ", skipping...");
             }
         }
         return preKeyRecords;
@@ -286,7 +335,8 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
 
     public static Pair<X509Certificate[], byte[]> verification(final Iq packet) {
         Element item = getItem(packet);
-        Element verification = item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null;
+        Element verification =
+                item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null;
         Element chain = verification != null ? verification.findChild("chain") : null;
         String signature = verification != null ? verification.findChildContent("signature") : null;
         if (chain != null && signature != null) {
@@ -300,7 +350,11 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                     if (cert == null) {
                         continue;
                     }
-                    certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(BaseEncoding.base64().decode(cert)));
+                    certificates[i] =
+                            (X509Certificate)
+                                    certificateFactory.generateCertificate(
+                                            new ByteArrayInputStream(
+                                                    BaseEncoding.base64().decode(cert)));
                     ++i;
                 }
                 return new Pair<>(certificates, BaseEncoding.base64().decode(signature));
@@ -332,8 +386,15 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 || signedPreKeySignature.length == 0) {
             return null;
         }
-        return new PreKeyBundle(0, 0, 0, null,
-                signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
+        return new PreKeyBundle(
+                0,
+                0,
+                0,
+                null,
+                signedPreKeyId,
+                signedPreKeyPublic,
+                signedPreKeySignature,
+                identityKey);
     }
 
     public static List<PreKeyBundle> preKeys(final Iq preKeys) {
@@ -342,8 +403,7 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         if (preKeyPublics != null) {
             for (Integer preKeyId : preKeyPublics.keySet()) {
                 ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
-                bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
-                        0, null, null, null));
+                bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic, 0, null, null, null));
             }
         }
 
@@ -363,15 +423,19 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 account.getRoster().markAllAsNotInRoster();
             }
             this.rosterItems(account, query);
-        } else if ((packet.hasChild("block", Namespace.BLOCKING) || packet.hasChild("blocklist", Namespace.BLOCKING)) &&
-                packet.fromServer(account)) {
+        } else if ((packet.hasChild("block", Namespace.BLOCKING)
+                        || packet.hasChild("blocklist", Namespace.BLOCKING))
+                && packet.fromServer(account)) {
             // Block list or block push.
             Log.d(Config.LOGTAG, "Received blocklist update from server");
             final Element blocklist = packet.findChild("blocklist", Namespace.BLOCKING);
             final Element block = packet.findChild("block", Namespace.BLOCKING);
-            final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
-                    (block != null ? block.getChildren() : null);
-            // If this is a response to a blocklist query, clear the block list and replace with the new one.
+            final Collection<Element> items =
+                    blocklist != null
+                            ? blocklist.getChildren()
+                            : (block != null ? block.getChildren() : null);
+            // If this is a response to a blocklist query, clear the block list and replace with the
+            // new one.
             // Otherwise, just update the existing blocklist.
             if (packet.getType() == Iq.Type.RESULT) {
                 account.clearBlocklist();
@@ -382,7 +446,8 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 // Create a collection of Jids from the packet
                 for (final Element item : items) {
                     if (item.getName().equals("item")) {
-                        final Jid jid = InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid"));
+                        final Jid jid =
+                                Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("jid"));
                         if (jid != null) {
                             jids.add(jid);
                         }
@@ -405,10 +470,12 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 final Iq response = packet.generateResponse(Iq.Type.RESULT);
                 mXmppConnectionService.sendIqPacket(account, response, null);
             }
-        } else if (packet.hasChild("unblock", Namespace.BLOCKING) &&
-                packet.fromServer(account) && packet.getType() == Iq.Type.SET) {
+        } else if (packet.hasChild("unblock", Namespace.BLOCKING)
+                && packet.fromServer(account)
+                && packet.getType() == Iq.Type.SET) {
             Log.d(Config.LOGTAG, "Received unblock update from server");
-            final Collection<Element> items = packet.findChild("unblock", Namespace.BLOCKING).getChildren();
+            final Collection<Element> items =
+                    packet.findChild("unblock", Namespace.BLOCKING).getChildren();
             if (items.isEmpty()) {
                 // No children to unblock == unblock all
                 account.getBlocklist().clear();
@@ -416,7 +483,8 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 final Collection<Jid> jids = new ArrayList<>(items.size());
                 for (final Element item : items) {
                     if (item.getName().equals("item")) {
-                        final Jid jid = InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid"));
+                        final Jid jid =
+                                Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("jid"));
                         if (jid != null) {
                             jids.add(jid);
                         }
@@ -430,10 +498,10 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
         } else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
                 || packet.hasChild("data", "http://jabber.org/protocol/ibb")
                 || packet.hasChild("close", "http://jabber.org/protocol/ibb")) {
-            mXmppConnectionService.getJingleConnectionManager()
-                    .deliverIbbPacket(account, packet);
+            mXmppConnectionService.getJingleConnectionManager().deliverIbbPacket(account, packet);
         } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
-            final Iq response = mXmppConnectionService.getIqGenerator().discoResponse(account, packet);
+            final Iq response =
+                    mXmppConnectionService.getIqGenerator().discoResponse(account, packet);
             mXmppConnectionService.sendIqPacket(account, response, null);
         } else if (packet.hasChild("query", "jabber:iq:version") && isGet) {
             final Iq response = mXmppConnectionService.getIqGenerator().versionResponse(packet);
@@ -452,7 +520,8 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                 response = mXmppConnectionService.getIqGenerator().entityTimeResponse(packet);
             }
             mXmppConnectionService.sendIqPacket(account, response, null);
-        } else if (packet.hasChild("push", Namespace.UNIFIED_PUSH) && packet.getType() == Iq.Type.SET) {
+        } else if (packet.hasChild("push", Namespace.UNIFIED_PUSH)
+                && packet.getType() == Iq.Type.SET) {
             final Jid transport = packet.getFrom();
             final Element push = packet.findChild("push", Namespace.UNIFIED_PUSH);
             final boolean success =
@@ -480,5 +549,4 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
             }
         }
     }
-
 }
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 80430e384..9188c1b66 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -31,7 +31,6 @@ import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.LocalizedContent;
 import eu.siacs.conversations.xml.Namespace;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.chatstate.ChatState;
 import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
@@ -95,7 +94,7 @@ public class MessageParser extends AbstractParser
         for (Element child : packet.getChildren()) {
             if (child.getName().equals("stanza-id")
                     && Namespace.STANZA_IDS.equals(child.getNamespace())
-                    && by.equals(InvalidJid.getNullForInvalid(child.getAttributeAsJid("by")))) {
+                    && by.equals(Jid.Invalid.getNullForInvalid(child.getAttributeAsJid("by")))) {
                 return child.getAttribute("id");
             }
         }
@@ -105,7 +104,7 @@ public class MessageParser extends AbstractParser
     private static Jid getTrueCounterpart(Element mucUserElement, Jid fallback) {
         final Element item = mucUserElement == null ? null : mucUserElement.findChild("item");
         Jid result =
-                item == null ? null : InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid"));
+                item == null ? null : Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("jid"));
         return result != null ? result : fallback;
     }
 
@@ -154,7 +153,7 @@ public class MessageParser extends AbstractParser
         final XmppAxolotlMessage xmppAxolotlMessage;
         try {
             xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.asBareJid());
-        } catch (Exception e) {
+        } catch (final Exception e) {
             Log.d(
                     Config.LOGTAG,
                     conversation.getAccount().getJid().asBareJid()
@@ -224,13 +223,13 @@ public class MessageParser extends AbstractParser
             final Element invite = mucUser.findChild("invite");
             if (invite != null) {
                 final String password = mucUser.findChildContent("password");
-                final Jid from = InvalidJid.getNullForInvalid(invite.getAttributeAsJid("from"));
-                final Jid to = InvalidJid.getNullForInvalid(invite.getAttributeAsJid("to"));
+                final Jid from = Jid.Invalid.getNullForInvalid(invite.getAttributeAsJid("from"));
+                final Jid to = Jid.Invalid.getNullForInvalid(invite.getAttributeAsJid("to"));
                 if (to != null && from == null) {
                     Log.d(Config.LOGTAG, "do not parse outgoing mediated invite " + message);
                     return null;
                 }
-                final Jid room = InvalidJid.getNullForInvalid(message.getAttributeAsJid("from"));
+                final Jid room = Jid.Invalid.getNullForInvalid(message.getAttributeAsJid("from"));
                 if (room == null) {
                     return null;
                 }
@@ -239,8 +238,8 @@ public class MessageParser extends AbstractParser
         }
         final Element conference = message.findChild("x", "jabber:x:conference");
         if (conference != null) {
-            Jid from = InvalidJid.getNullForInvalid(message.getAttributeAsJid("from"));
-            Jid room = InvalidJid.getNullForInvalid(conference.getAttributeAsJid("jid"));
+            Jid from = Jid.Invalid.getNullForInvalid(message.getAttributeAsJid("from"));
+            Jid room = Jid.Invalid.getNullForInvalid(conference.getAttributeAsJid("jid"));
             if (room == null) {
                 return null;
             }
@@ -333,7 +332,7 @@ public class MessageParser extends AbstractParser
                 }
             }
             if (retract != null) {
-                final Jid id = InvalidJid.getNullForInvalid(retract.getAttributeAsJid("id"));
+                final Jid id = Jid.Invalid.getNullForInvalid(retract.getAttributeAsJid("id"));
                 if (id != null) {
                     account.removeBookmark(id);
                     Log.d(
@@ -567,7 +566,7 @@ public class MessageParser extends AbstractParser
         }
         boolean notify = false;
 
-        if (from == null || !InvalidJid.isValid(from) || !InvalidJid.isValid(to)) {
+        if (from == null || !Jid.Invalid.isValid(from) || !Jid.Invalid.isValid(to)) {
             Log.e(Config.LOGTAG, "encountered invalid message from='" + from + "' to='" + to + "'");
             return;
         }
@@ -607,7 +606,7 @@ public class MessageParser extends AbstractParser
             occupant = null;
         }
         boolean isMucStatusMessage =
-                InvalidJid.hasValidFrom(packet)
+                Jid.Invalid.hasValidFrom(packet)
                         && from.isBareJid()
                         && mucUserElement != null
                         && mucUserElement.hasChild("status");
@@ -668,7 +667,7 @@ public class MessageParser extends AbstractParser
                             || mucUserElement != null
                             || account.getXmppConnection()
                                     .getMucServersWithholdAccount()
-                                    .contains(counterpart.getDomain().toEscapedString());
+                                    .contains(counterpart.getDomain().toString());
             final Conversation conversation =
                     mXmppConnectionService.findOrCreateConversation(
                             account,
@@ -1161,7 +1160,7 @@ public class MessageParser extends AbstractParser
             }
             if (conversation != null
                     && mucUserElement != null
-                    && InvalidJid.hasValidFrom(packet)
+                    && Jid.Invalid.hasValidFrom(packet)
                     && from.isBareJid()) {
                 for (Element child : mucUserElement.getChildren()) {
                     if ("status".equals(child.getName())) {
@@ -1381,7 +1380,7 @@ public class MessageParser extends AbstractParser
 
         final Element event =
                 original.findChild("event", "http://jabber.org/protocol/pubsub#event");
-        if (event != null && InvalidJid.hasValidFrom(original) && original.getFrom().isBareJid()) {
+        if (event != null && Jid.Invalid.hasValidFrom(original) && original.getFrom().isBareJid()) {
             if (event.hasChild("items")) {
                 parseEvent(event, original.getFrom(), account);
             } else if (event.hasChild("delete")) {
@@ -1392,7 +1391,7 @@ public class MessageParser extends AbstractParser
         }
 
         final String nick = packet.findChildContent("nick", Namespace.NICK);
-        if (nick != null && InvalidJid.hasValidFrom(original)) {
+        if (nick != null && Jid.Invalid.hasValidFrom(original)) {
             if (mXmppConnectionService.isMuc(account, from)) {
                 return;
             }
@@ -1444,7 +1443,7 @@ public class MessageParser extends AbstractParser
             Jid from) {
         final var id = displayed.getId();
         // TODO we don’t even use 'sender' any more. Remove this!
-        final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender"));
+        final Jid sender = Jid.Invalid.getNullForInvalid(displayed.getAttributeAsJid("sender"));
         if (packet.fromAccount(account) && !selfAddressed) {
             final Conversation c = mXmppConnectionService.find(account, counterpart.asBareJid());
             final Message message =
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index e50d1096c..088938402 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -1,7 +1,6 @@
 package eu.siacs.conversations.parser;
 
 import android.util.Log;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.crypto.PgpEngine;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -17,24 +16,23 @@ import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.Namespace;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.pep.Avatar;
 import im.conversations.android.xmpp.model.occupant.OccupantId;
-
-import org.openintents.openpgp.util.OpenPgpUtils;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
+import org.openintents.openpgp.util.OpenPgpUtils;
 
-public class PresenceParser extends AbstractParser implements Consumer<im.conversations.android.xmpp.model.stanza.Presence> {
+public class PresenceParser extends AbstractParser
+        implements Consumer<im.conversations.android.xmpp.model.stanza.Presence> {
 
     public PresenceParser(final XmppConnectionService service, final Account account) {
         super(service, account);
     }
 
-    public void parseConferencePresence(final im.conversations.android.xmpp.model.stanza.Presence packet, Account account) {
+    public void parseConferencePresence(
+            final im.conversations.android.xmpp.model.stanza.Presence packet, Account account) {
         final Conversation conversation =
                 packet.getFrom() == null
                         ? null
@@ -58,7 +56,9 @@ public class PresenceParser extends AbstractParser implements Consumer<im.conver
         }
     }
 
-    private void processConferencePresence(final im.conversations.android.xmpp.model.stanza.Presence packet, Conversation conversation) {
+    private void processConferencePresence(
+            final im.conversations.android.xmpp.model.stanza.Presence packet,
+            Conversation conversation) {
         final Account account = conversation.getAccount();
         final MucOptions mucOptions = conversation.getMucOptions();
         final Jid jid = conversation.getAccount().getJid();
@@ -75,12 +75,15 @@ public class PresenceParser extends AbstractParser implements Consumer<im.conver
                         mucOptions.setError(MucOptions.Error.NONE);
                         final MucOptions.User user = parseItem(conversation, item, from);
                         final var occupant = packet.getExtension(OccupantId.class);
-                        final String occupantId = mucOptions.occupantId() && occupant != null ? occupant.getId() : null;
+                        final String occupantId =
+                                mucOptions.occupantId() && occupant != null
+                                        ? occupant.getId()
+                                        : null;
                         user.setOccupantId(occupantId);
                         if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE)
                                 || (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED)
                                         && jid.equals(
-                                                InvalidJid.getNullForInvalid(
+                                                Jid.Invalid.getNullForInvalid(
                                                         item.getAttributeAsJid("jid"))))) {
                             if (mucOptions.setOnline()) {
                                 mXmppConnectionService.getAvatarService().clear(mucOptions);
@@ -91,7 +94,8 @@ public class PresenceParser extends AbstractParser implements Consumer<im.conver
                                 mXmppConnectionService.databaseBackend.updateConversation(
                                         conversation);
                             }
-                            final var modified = current == null || !current.equals(user.getFullJid());
+                            final var modified =
+                                    current == null || !current.equals(user.getFullJid());
                             mXmppConnectionService.persistSelfNick(user, modified);
                             invokeRenameListener(mucOptions, true);
                         }
@@ -166,7 +170,7 @@ public class PresenceParser extends AbstractParser implements Consumer<im.conver
                     final Jid alternate =
                             destroy == null
                                     ? null
-                                    : InvalidJid.getNullForInvalid(
+                                    : Jid.Invalid.getNullForInvalid(
                                             destroy.getAttributeAsJid("jid"));
                     mucOptions.setError(MucOptions.Error.DESTROYED);
                     if (alternate != null) {
@@ -301,7 +305,9 @@ public class PresenceParser extends AbstractParser implements Consumer<im.conver
         return codes;
     }
 
-    private void parseContactPresence(final im.conversations.android.xmpp.model.stanza.Presence packet, final Account account) {
+    private void parseContactPresence(
+            final im.conversations.android.xmpp.model.stanza.Presence packet,
+            final Account account) {
         final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator();
         final Jid from = packet.getFrom();
         if (from == null || from.equals(account.getJid())) {
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 9e3fd1e40..4982a4463 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -29,7 +29,6 @@ import eu.siacs.conversations.utils.CursorUtils;
 import eu.siacs.conversations.utils.FtsUtils;
 import eu.siacs.conversations.utils.MimeUtils;
 import eu.siacs.conversations.utils.Resolver;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.mam.MamReference;
 import java.io.ByteArrayInputStream;
@@ -1142,7 +1141,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
                                         cursor.getString(cursor.getColumnIndex(Account.SERVER)),
                                         null)
                                 .getDomain()
-                                .toEscapedString();
+                                .toString();
             } catch (IllegalArgumentException ignored) {
                 Log.e(
                         Config.LOGTAG,
@@ -1307,7 +1306,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
                         selectionArgs);
         while (cursor.moveToNext()) {
             final Conversation conversation = Conversation.fromCursor(cursor);
-            if (conversation.getJid() instanceof InvalidJid) {
+            if (conversation.getJid() instanceof Jid.Invalid) {
                 continue;
             }
             list.add(conversation);
@@ -1616,7 +1615,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
             }
             cursor.moveToFirst();
             final Conversation conversation = Conversation.fromCursor(cursor);
-            if (conversation.getJid() instanceof InvalidJid) {
+            if (conversation.getJid() instanceof Jid.Invalid) {
                 return null;
             }
             return conversation;
@@ -1649,7 +1648,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
             }
             cursor.moveToFirst();
             final Conversation conversation = Conversation.fromCursor(cursor);
-            if (conversation.getJid() instanceof InvalidJid) {
+            if (conversation.getJid() instanceof Jid.Invalid) {
                 return null;
             }
             conversation.setAccount(account);
diff --git a/src/main/java/eu/siacs/conversations/persistance/UnifiedPushDatabase.java b/src/main/java/eu/siacs/conversations/persistance/UnifiedPushDatabase.java
index 6535a5c80..4d7dc7a25 100644
--- a/src/main/java/eu/siacs/conversations/persistance/UnifiedPushDatabase.java
+++ b/src/main/java/eu/siacs/conversations/persistance/UnifiedPushDatabase.java
@@ -6,19 +6,15 @@ import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.services.UnifiedPushBroker;
+import java.util.List;
 
 public class UnifiedPushDatabase extends SQLiteOpenHelper {
     private static final String DATABASE_NAME = "unified-push-distributor";
@@ -42,7 +38,9 @@ public class UnifiedPushDatabase extends SQLiteOpenHelper {
     @Override
     public void onCreate(final SQLiteDatabase sqLiteDatabase) {
         sqLiteDatabase.execSQL(
-                "CREATE TABLE push (account TEXT, transport TEXT, application TEXT NOT NULL, instance TEXT NOT NULL UNIQUE, endpoint TEXT, expiration NUMBER DEFAULT 0)");
+                "CREATE TABLE push (account TEXT, transport TEXT, application TEXT NOT NULL,"
+                        + " instance TEXT NOT NULL UNIQUE, endpoint TEXT, expiration NUMBER DEFAULT"
+                        + " 0)");
     }
 
     public boolean register(final String application, final String instance) {
@@ -113,7 +111,8 @@ public class UnifiedPushDatabase extends SQLiteOpenHelper {
                 sqLiteDatabase.query(
                         "push",
                         new String[] {"application", "endpoint"},
-                        "account = ? AND transport = ? AND instance = ? AND endpoint IS NOT NULL AND expiration >= "
+                        "account = ? AND transport = ? AND instance = ? AND endpoint IS NOT NULL"
+                                + " AND expiration >= "
                                 + expiration,
                         new String[] {account, transport, instance},
                         null,
@@ -131,17 +130,26 @@ public class UnifiedPushDatabase extends SQLiteOpenHelper {
     public List<PushTarget> deletePushTargets() {
         final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
         final ImmutableList.Builder<PushTarget> builder = new ImmutableList.Builder<>();
-        try (final Cursor cursor = sqLiteDatabase.query("push",new String[]{"application","instance"},null,null,null,null,null)) {
+        try (final Cursor cursor =
+                sqLiteDatabase.query(
+                        "push",
+                        new String[] {"application", "instance"},
+                        null,
+                        null,
+                        null,
+                        null,
+                        null)) {
             if (cursor != null && cursor.moveToFirst()) {
-                builder.add(new PushTarget(
-                        cursor.getString(cursor.getColumnIndexOrThrow("application")),
-                        cursor.getString(cursor.getColumnIndexOrThrow("instance"))));
+                builder.add(
+                        new PushTarget(
+                                cursor.getString(cursor.getColumnIndexOrThrow("application")),
+                                cursor.getString(cursor.getColumnIndexOrThrow("instance"))));
             }
         } catch (final Exception e) {
-            Log.d(Config.LOGTAG,"unable to retrieve push targets",e);
+            Log.d(Config.LOGTAG, "unable to retrieve push targets", e);
             return builder.build();
         }
-        sqLiteDatabase.delete("push",null,null);
+        sqLiteDatabase.delete("push", null, null);
         return builder.build();
     }
 
@@ -149,9 +157,10 @@ public class UnifiedPushDatabase extends SQLiteOpenHelper {
         final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
         try (final Cursor cursor =
                 sqLiteDatabase.rawQuery(
-                        "SELECT EXISTS(SELECT endpoint FROM push WHERE account = ? AND transport = ?)",
+                        "SELECT EXISTS(SELECT endpoint FROM push WHERE account = ? AND transport ="
+                                + " ?)",
                         new String[] {
-                            transport.account.getUuid(), transport.transport.toEscapedString()
+                            transport.account.getUuid(), transport.transport.toString()
                         })) {
             if (cursor != null && cursor.moveToFirst()) {
                 return cursor.getInt(0) > 0;
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index e9e827c56..460215576 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -16,17 +16,9 @@ import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.LruCache;
-
 import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
 import androidx.core.content.res.ResourcesCompat;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
@@ -43,647 +35,706 @@ import eu.siacs.conversations.utils.UIHelper;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 import eu.siacs.conversations.xmpp.XmppConnection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
 
 public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
 
-	private static final int FG_COLOR = 0xFFFAFAFA;
-	private static final int TRANSPARENT = 0x00000000;
-	private static final int PLACEHOLDER_COLOR = 0xFF202020;
-
-	public static final int SYSTEM_UI_AVATAR_SIZE = 48;
-
-	private static final String PREFIX_CONTACT = "contact";
-	private static final String PREFIX_CONVERSATION = "conversation";
-	private static final String PREFIX_ACCOUNT = "account";
-	private static final String PREFIX_GENERIC = "generic";
-
-	private static final String CHANNEL_SYMBOL = "#";
-
-	final private Set<Integer> sizes = new HashSet<>();
-	final private HashMap<String, Set<String>> conversationDependentKeys = new HashMap<>();
-
-	protected XmppConnectionService mXmppConnectionService = null;
-
-	AvatarService(XmppConnectionService service) {
-		this.mXmppConnectionService = service;
-	}
-
-	public static int getSystemUiAvatarSize(final Context context) {
-		return (int) (SYSTEM_UI_AVATAR_SIZE * context.getResources().getDisplayMetrics().density);
-	}
-
-	public Bitmap get(final Avatarable avatarable, final int size, final boolean cachedOnly) {
-		if (avatarable instanceof Account) {
-			return get((Account) avatarable,size,cachedOnly);
-		} else if (avatarable instanceof Conversation) {
-			return get((Conversation) avatarable, size, cachedOnly);
-		} else if (avatarable instanceof Message) {
-			return get((Message) avatarable, size, cachedOnly);
-		} else if (avatarable instanceof ListItem) {
-			return get((ListItem) avatarable, size, cachedOnly);
-		} else if (avatarable instanceof MucOptions.User) {
-			return get((MucOptions.User) avatarable, size, cachedOnly);
-		} else if (avatarable instanceof Room) {
-			return get((Room) avatarable, size, cachedOnly);
-		}
-		throw new AssertionError("AvatarService does not know how to generate avatar from "+avatarable.getClass().getName());
-
-	}
-
-	private Bitmap get(final Room result, final int size, boolean cacheOnly) {
-		final Jid room = result.getRoom();
-		Conversation conversation = room != null ? mXmppConnectionService.findFirstMuc(room) : null;
-		if (conversation != null) {
-			return get(conversation,size,cacheOnly);
-		}
-		return get(CHANNEL_SYMBOL, room != null ? room.asBareJid().toEscapedString() : result.getName(), size, cacheOnly);
-	}
-
-	private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
-		if (contact.isSelf()) {
-			return get(contact.getAccount(), size, cachedOnly);
-		}
-		final String KEY = key(contact, size);
-		Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
-		if (avatar != null || cachedOnly) {
-			return avatar;
-		}
-		if (contact.getAvatarFilename() != null && QuickConversationsService.isQuicksy()) {
-			avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatarFilename(), size);
-		}
-		if (avatar == null && contact.getProfilePhoto() != null) {
-			avatar = mXmppConnectionService.getFileBackend().cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
-		}
-		if (avatar == null && contact.getAvatarFilename() != null) {
-			avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatarFilename(), size);
-		}
-		if (avatar == null) {
-			avatar = get(contact.getDisplayName(), contact.getJid().asBareJid().toString(), size, false);
-		}
-		this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
-		return avatar;
-	}
-
-	public Bitmap getRoundedShortcut(final MucOptions mucOptions) {
-		final DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
-		final int size = Math.round(metrics.density * 48);
-		final Bitmap bitmap = get(mucOptions, size, false);
-		final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
-		final Canvas canvas = new Canvas(output);
-		final Paint paint = new Paint();
-		drawAvatar(bitmap, canvas, paint);
-		return output;
-	}
-
-	public Bitmap getRoundedShortcut(final Contact contact) {
-		return getRoundedShortcut(contact, false);
-	}
-
-	public Bitmap getRoundedShortcutWithIcon(final Contact contact) {
-		return getRoundedShortcut(contact, true);
-	}
-
-	private Bitmap getRoundedShortcut(final Contact contact, boolean withIcon) {
-		DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
-		int size = Math.round(metrics.density * 48);
-		Bitmap bitmap = get(contact, size);
-		Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
-		Canvas canvas = new Canvas(output);
-		final Paint paint = new Paint();
-
-		drawAvatar(bitmap, canvas, paint);
-		if (withIcon) {
-			drawIcon(canvas, paint);
-		}
-		return output;
-	}
-
-	private static void drawAvatar(Bitmap bitmap, Canvas canvas, Paint paint) {
-		final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
-		paint.setAntiAlias(true);
-		canvas.drawARGB(0, 0, 0, 0);
-		canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
-		paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
-		canvas.drawBitmap(bitmap, rect, rect, paint);
-	}
-
-	private void drawIcon(Canvas canvas, Paint paint) {
-		final Resources resources = mXmppConnectionService.getResources();
-		final Bitmap icon = getRoundLauncherIcon(resources);
-		if (icon == null) {
-			return;
-		}
-		paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
-
-		int iconSize = Math.round(canvas.getHeight() / 2.6f);
-
-		int left = canvas.getWidth() - iconSize;
-		int top = canvas.getHeight() - iconSize;
-		final Rect rect = new Rect(left, top, left + iconSize, top + iconSize);
-		canvas.drawBitmap(icon, null, rect, paint);
-	}
-
-	private static Bitmap getRoundLauncherIcon(Resources resources) {
-
-		final Drawable drawable = ResourcesCompat.getDrawable(resources, R.mipmap.new_launcher_round,null);
-		if (drawable == null) {
-			return null;
-		}
-
-		if (drawable instanceof BitmapDrawable) {
-			return ((BitmapDrawable)drawable).getBitmap();
-		}
-
-		Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-		Canvas canvas = new Canvas(bitmap);
-		drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-		drawable.draw(canvas);
-
-		return bitmap;
-	}
-
-	public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
-		Contact c = user.getContact();
-		if (c != null && (c.getProfilePhoto() != null || c.getAvatarFilename() != null || user.getAvatar() == null)) {
-			return get(c, size, cachedOnly);
-		} else {
-			return getImpl(user, size, cachedOnly);
-		}
-	}
-
-	private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) {
-		final String KEY = key(user, size);
-		Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
-		if (avatar != null || cachedOnly) {
-			return avatar;
-		}
-		if (user.getAvatar() != null) {
-			avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
-		}
-		if (avatar == null) {
-			Contact contact = user.getContact();
-			if (contact != null) {
-				avatar = get(contact, size, false);
-			} else {
-				String seed = user.getRealJid() != null ? user.getRealJid().asBareJid().toString() : null;
-				avatar = get(user.getName(), seed, size, false);
-			}
-		}
-		this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
-		return avatar;
-	}
-
-	public void clear(Contact contact) {
-		synchronized (this.sizes) {
-			for (final Integer size : sizes) {
-				this.mXmppConnectionService.getBitmapCache().remove(key(contact, size));
-			}
-		}
-		for (Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
-			MucOptions.User user = conversation.getMucOptions().findUserByRealJid(contact.getJid().asBareJid());
-			if (user != null) {
-				clear(user);
-			}
-			clear(conversation);
-		}
-	}
-
-	private String key(Contact contact, int size) {
-		synchronized (this.sizes) {
-			this.sizes.add(size);
-		}
-		return PREFIX_CONTACT +
-				'\0' +
-				contact.getAccount().getJid().asBareJid() +
-				'\0' +
-				emptyOnNull(contact.getJid()) +
-				'\0' +
-				size;
-	}
-
-	private String key(MucOptions.User user, int size) {
-		synchronized (this.sizes) {
-			this.sizes.add(size);
-		}
-		return PREFIX_CONTACT +
-				'\0' +
-				user.getAccount().getJid().asBareJid() +
-				'\0' +
-				emptyOnNull(user.getFullJid()) +
-				'\0' +
-				emptyOnNull(user.getRealJid()) +
-				'\0' +
-				size;
-	}
-
-	public Bitmap get(ListItem item, int size) {
-		return get(item, size, false);
-	}
-
-	public Bitmap get(ListItem item, int size, boolean cachedOnly) {
-		if (item instanceof RawBlockable) {
-			return get(item.getDisplayName(), item.getJid().toEscapedString(), size, cachedOnly);
-		} else if (item instanceof Contact) {
-			return get((Contact) item, size, cachedOnly);
-		} else if (item instanceof Bookmark) {
-			Bookmark bookmark = (Bookmark) item;
-			if (bookmark.getConversation() != null) {
-				return get(bookmark.getConversation(), size, cachedOnly);
-			} else {
-				Jid jid = bookmark.getJid();
-				Account account = bookmark.getAccount();
-				Contact contact = jid == null ? null : account.getRoster().getContact(jid);
-				if (contact != null && contact.getAvatarFilename() != null) {
-					return get(contact, size, cachedOnly);
-				}
-				String seed = jid != null ? jid.asBareJid().toString() : null;
-				return get(bookmark.getDisplayName(), seed, size, cachedOnly);
-			}
-		} else {
-			String seed = item.getJid() != null ? item.getJid().asBareJid().toString() : null;
-			return get(item.getDisplayName(), seed, size, cachedOnly);
-		}
-	}
-
-	public Bitmap get(Conversation conversation, int size) {
-		return get(conversation, size, false);
-	}
-
-	public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
-		if (conversation.getMode() == Conversation.MODE_SINGLE) {
-			return get(conversation.getContact(), size, cachedOnly);
-		} else {
-			return get(conversation.getMucOptions(), size, cachedOnly);
-		}
-	}
-
-	public void clear(Conversation conversation) {
-		if (conversation.getMode() == Conversation.MODE_SINGLE) {
-			clear(conversation.getContact());
-		} else {
-			clear(conversation.getMucOptions());
-			synchronized (this.conversationDependentKeys) {
-				Set<String> keys = this.conversationDependentKeys.get(conversation.getUuid());
-				if (keys == null) {
-					return;
-				}
-				LruCache<String, Bitmap> cache = this.mXmppConnectionService.getBitmapCache();
-				for (String key : keys) {
-					cache.remove(key);
-				}
-				keys.clear();
-			}
-		}
-	}
-
-	private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
-		final String KEY = key(mucOptions, size);
-		Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
-		if (bitmap != null || cachedOnly) {
-			return bitmap;
-		}
-
-		bitmap = mXmppConnectionService.getFileBackend().getAvatar(mucOptions.getAvatar(), size);
-
-		if (bitmap == null) {
-			Conversation c = mucOptions.getConversation();
-			if (mucOptions.isPrivateAndNonAnonymous()) {
-				final List<MucOptions.User> users = mucOptions.getUsersRelevantForNameAndAvatar();
-				if (users.size() == 0) {
-					bitmap = getImpl(c.getName().toString(), c.getJid().asBareJid().toString(), size);
-				} else {
-					bitmap = getImpl(users, size);
-				}
-			} else {
-				bitmap = getImpl(CHANNEL_SYMBOL, c.getJid().asBareJid().toString(), size);
-			}
-		}
-
-		this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
-
-		return bitmap;
-	}
-
-	private Bitmap get(List<MucOptions.User> users, int size, boolean cachedOnly) {
-		final String KEY = key(users, size);
-		Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
-		if (bitmap != null || cachedOnly) {
-			return bitmap;
-		}
-		bitmap = getImpl(users, size);
-		this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
-		return bitmap;
-	}
-
-	private Bitmap getImpl(List<MucOptions.User> users, int size) {
-		int count = users.size();
-		Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-		Canvas canvas = new Canvas(bitmap);
-		bitmap.eraseColor(TRANSPARENT);
-		if (count == 0) {
-			throw new AssertionError("Unable to draw tiles for 0 users");
-		} else if (count == 1) {
-			drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
-			drawTile(canvas, users.get(0).getAccount(), size / 2 + 1, 0, size, size);
-		} else if (count == 2) {
-			drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
-			drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
-		} else if (count == 3) {
-			drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
-			drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
-			drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size,
-					size);
-		} else if (count == 4) {
-			drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
-			drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
-			drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
-			drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size,
-					size);
-		} else {
-			drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
-			drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
-			drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
-			drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1,
-					size, size);
-		}
-		return bitmap;
-	}
-
-	public void clear(final MucOptions options) {
-		if (options == null) {
-			return;
-		}
-		synchronized (this.sizes) {
-			for (Integer size : sizes) {
-				this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
-			}
-		}
-	}
-
-	private String key(final MucOptions options, int size) {
-		synchronized (this.sizes) {
-			this.sizes.add(size);
-		}
-		return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size;
-	}
-
-	private String key(List<MucOptions.User> users, int size) {
-		final Conversation conversation = users.get(0).getConversation();
-		StringBuilder builder = new StringBuilder("TILE_");
-		builder.append(conversation.getUuid());
-
-		for (MucOptions.User user : users) {
-			builder.append("\0");
-			builder.append(emptyOnNull(user.getRealJid()));
-			builder.append("\0");
-			builder.append(emptyOnNull(user.getFullJid()));
-		}
-		builder.append('\0');
-		builder.append(size);
-		final String key = builder.toString();
-		synchronized (this.conversationDependentKeys) {
-			Set<String> keys;
-			if (this.conversationDependentKeys.containsKey(conversation.getUuid())) {
-				keys = this.conversationDependentKeys.get(conversation.getUuid());
-			} else {
-				keys = new HashSet<>();
-				this.conversationDependentKeys.put(conversation.getUuid(), keys);
-			}
-			keys.add(key);
-		}
-		return key;
-	}
-
-	public Bitmap get(Account account, int size) {
-		return get(account, size, false);
-	}
-
-	public Bitmap get(Account account, int size, boolean cachedOnly) {
-		final String KEY = key(account, size);
-		Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
-		if (avatar != null || cachedOnly) {
-			return avatar;
-		}
-		avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
-		if (avatar == null) {
-			final String displayName = account.getDisplayName();
-			final String jid = account.getJid().asBareJid().toEscapedString();
-			if (QuickConversationsService.isQuicksy() && !TextUtils.isEmpty(displayName)) {
-				avatar = get(displayName, jid, size, false);
-			} else {
-				avatar = get(jid, null, size, false);
-			}
-		}
-		mXmppConnectionService.getBitmapCache().put(KEY, avatar);
-		return avatar;
-	}
-
-	public Bitmap get(Message message, int size, boolean cachedOnly) {
-		final Conversational conversation = message.getConversation();
-		if (message.getType() == Message.TYPE_STATUS && message.getCounterparts() != null && message.getCounterparts().size() > 1) {
-			return get(message.getCounterparts(), size, cachedOnly);
-		} else if (message.getStatus() == Message.STATUS_RECEIVED) {
-			Contact c = message.getContact();
-			if (c != null && (c.getProfilePhoto() != null || c.getAvatarFilename() != null)) {
-				return get(c, size, cachedOnly);
-			} else if (conversation instanceof Conversation && message.getConversation().getMode() == Conversation.MODE_MULTI) {
-				final Jid trueCounterpart = message.getTrueCounterpart();
-				final MucOptions mucOptions = ((Conversation) conversation).getMucOptions();
-				MucOptions.User user;
-				if (trueCounterpart != null) {
-					user = mucOptions.findOrCreateUserByRealJid(trueCounterpart, message.getCounterpart());
-				} else {
-					user = mucOptions.findUserByFullJid(message.getCounterpart());
-				}
-				if (user != null) {
-					return getImpl(user, size, cachedOnly);
-				}
-			} else if (c != null) {
-				return get(c, size, cachedOnly);
-			}
-			Jid tcp = message.getTrueCounterpart();
-			String seed = tcp != null ? tcp.asBareJid().toString() : null;
-			return get(UIHelper.getMessageDisplayName(message), seed, size, cachedOnly);
-		} else {
-			return get(conversation.getAccount(), size, cachedOnly);
-		}
-	}
-
-	public void clear(Account account) {
-		synchronized (this.sizes) {
-			for (Integer size : sizes) {
-				this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
-			}
-		}
-	}
-
-	public void clear(MucOptions.User user) {
-		synchronized (this.sizes) {
-			for (Integer size : sizes) {
-				this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
-			}
-		}
-	}
-
-	private String key(Account account, int size) {
-		synchronized (this.sizes) {
-			this.sizes.add(size);
-		}
-		return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
-				+ size;
-	}
-
-	/*public Bitmap get(String name, int size) {
-		return get(name,null, size,false);
-	}*/
-
-	public Bitmap get(final String name, String seed, final int size, boolean cachedOnly) {
-		final String KEY = key(seed == null ? name : name+"\0"+seed, size);
-		Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
-		if (bitmap != null || cachedOnly) {
-			return bitmap;
-		}
-		bitmap = getImpl(name, seed, size);
-		mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
-		return bitmap;
-	}
-
-	public static Bitmap get(final Jid jid, final int size) {
-		return getImpl(jid.asBareJid().toEscapedString(), null, size);
-	}
-
-	private static Bitmap getImpl(final String name, final String seed, final int size) {
-		Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-		Canvas canvas = new Canvas(bitmap);
-		final String trimmedName = name == null ? "" : name.trim();
-		drawTile(canvas, trimmedName, seed, 0, 0, size, size);
-		return bitmap;
-	}
-
-	private String key(String name, int size) {
-		synchronized (this.sizes) {
-			this.sizes.add(size);
-		}
-		return PREFIX_GENERIC + "_" + name + "_" + size;
-	}
-
-	private static boolean drawTile(Canvas canvas, String letter, int tileColor, int left, int top, int right, int bottom) {
-		letter = letter.toUpperCase(Locale.getDefault());
-		Paint tilePaint = new Paint(), textPaint = new Paint();
-		tilePaint.setColor(tileColor);
-		textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-		textPaint.setColor(FG_COLOR);
-		textPaint.setTypeface(Typeface.create("sans-serif-light",
-				Typeface.NORMAL));
-		textPaint.setTextSize((float) ((right - left) * 0.8));
-		Rect rect = new Rect();
-
-		canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
-		textPaint.getTextBounds(letter, 0, 1, rect);
-		float width = textPaint.measureText(letter);
-		canvas.drawText(letter, (right + left) / 2 - width / 2, (top + bottom)
-				/ 2 + rect.height() / 2, textPaint);
-		return true;
-	}
-
-	private boolean drawTile(Canvas canvas, MucOptions.User user, int left, int top, int right, int bottom) {
-		Contact contact = user.getContact();
-		if (contact != null) {
-			Uri uri = null;
-			if (contact.getAvatarFilename() != null && QuickConversationsService.isQuicksy()) {
-				uri = mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename());
-			} else if (contact.getProfilePhoto() != null) {
-				uri = Uri.parse(contact.getProfilePhoto());
-			} else if (contact.getAvatarFilename() != null) {
-				uri = mXmppConnectionService.getFileBackend().getAvatarUri(contact.getAvatarFilename());
-			}
-			if (drawTile(canvas, uri, left, top, right, bottom)) {
-				return true;
-			}
-		} else if (user.getAvatar() != null) {
-			Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
-			if (drawTile(canvas, uri, left, top, right, bottom)) {
-				return true;
-			}
-		}
-		if (contact != null) {
-			String seed = contact.getJid().asBareJid().toString();
-			drawTile(canvas, contact.getDisplayName(), seed, left, top, right, bottom);
-		} else {
-			String seed = user.getRealJid() == null ? null : user.getRealJid().asBareJid().toString();
-			drawTile(canvas, user.getName(), seed, left, top, right, bottom);
-		}
-		return true;
-	}
-
-	private boolean drawTile(Canvas canvas, Account account, int left, int top, int right, int bottom) {
-		String avatar = account.getAvatar();
-		if (avatar != null) {
-			Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(avatar);
-			if (uri != null) {
-				if (drawTile(canvas, uri, left, top, right, bottom)) {
-					return true;
-				}
-			}
-		}
-		String name = account.getJid().asBareJid().toString();
-		return drawTile(canvas, name, name, left, top, right, bottom);
-	}
-
-	private static boolean drawTile(Canvas canvas, String name, String seed, int left, int top, int right, int bottom) {
-		if (name != null) {
-			final String letter = name.equals(CHANNEL_SYMBOL) ? name : getFirstLetter(name);
-			final int color = UIHelper.getColorForName(seed == null ? name : seed);
-			drawTile(canvas, letter, color, left, top, right, bottom);
-			return true;
-		}
-		return false;
-	}
-
-	private static String getFirstLetter(String name) {
-		for (Character c : name.toCharArray()) {
-			if (Character.isLetterOrDigit(c)) {
-				return c.toString();
-			}
-		}
-		return "X";
-	}
-
-	private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) {
-		if (uri != null) {
-			Bitmap bitmap = mXmppConnectionService.getFileBackend()
-					.cropCenter(uri, bottom - top, right - left);
-			if (bitmap != null) {
-				drawTile(canvas, bitmap, left, top, right, bottom);
-				return true;
-			}
-		}
-		return false;
-	}
-
-	private boolean drawTile(Canvas canvas, Bitmap bm, int dstleft, int dsttop, int dstright, int dstbottom) {
-		Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
-		canvas.drawBitmap(bm, null, dst, null);
-		return true;
-	}
-
-	@Override
-	public void onAdvancedStreamFeaturesAvailable(Account account) {
-		XmppConnection.Features features = account.getXmppConnection().getFeatures();
-		if (features.pep() && !features.pepPersistent()) {
-			Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": has pep but is not persistent");
-			if (account.getAvatar() != null) {
-				mXmppConnectionService.republishAvatarIfNeeded(account);
-			}
-		}
-	}
-
-	private static String emptyOnNull(@Nullable Jid value) {
-		return value == null ? "" : value.toString();
-	}
-
-	public interface Avatarable {
-		@ColorInt int getAvatarBackgroundColor();
-		String getAvatarName();
-	}
+    private static final int FG_COLOR = 0xFFFAFAFA;
+    private static final int TRANSPARENT = 0x00000000;
+    private static final int PLACEHOLDER_COLOR = 0xFF202020;
+
+    public static final int SYSTEM_UI_AVATAR_SIZE = 48;
+
+    private static final String PREFIX_CONTACT = "contact";
+    private static final String PREFIX_CONVERSATION = "conversation";
+    private static final String PREFIX_ACCOUNT = "account";
+    private static final String PREFIX_GENERIC = "generic";
+
+    private static final String CHANNEL_SYMBOL = "#";
+
+    private final Set<Integer> sizes = new HashSet<>();
+    private final HashMap<String, Set<String>> conversationDependentKeys = new HashMap<>();
+
+    protected XmppConnectionService mXmppConnectionService = null;
+
+    AvatarService(XmppConnectionService service) {
+        this.mXmppConnectionService = service;
+    }
+
+    public static int getSystemUiAvatarSize(final Context context) {
+        return (int) (SYSTEM_UI_AVATAR_SIZE * context.getResources().getDisplayMetrics().density);
+    }
+
+    public Bitmap get(final Avatarable avatarable, final int size, final boolean cachedOnly) {
+        if (avatarable instanceof Account) {
+            return get((Account) avatarable, size, cachedOnly);
+        } else if (avatarable instanceof Conversation) {
+            return get((Conversation) avatarable, size, cachedOnly);
+        } else if (avatarable instanceof Message) {
+            return get((Message) avatarable, size, cachedOnly);
+        } else if (avatarable instanceof ListItem) {
+            return get((ListItem) avatarable, size, cachedOnly);
+        } else if (avatarable instanceof MucOptions.User) {
+            return get((MucOptions.User) avatarable, size, cachedOnly);
+        } else if (avatarable instanceof Room) {
+            return get((Room) avatarable, size, cachedOnly);
+        }
+        throw new AssertionError(
+                "AvatarService does not know how to generate avatar from "
+                        + avatarable.getClass().getName());
+    }
+
+    private Bitmap get(final Room result, final int size, boolean cacheOnly) {
+        final Jid room = result.getRoom();
+        Conversation conversation = room != null ? mXmppConnectionService.findFirstMuc(room) : null;
+        if (conversation != null) {
+            return get(conversation, size, cacheOnly);
+        }
+        return get(
+                CHANNEL_SYMBOL,
+                room != null ? room.asBareJid().toString() : result.getName(),
+                size,
+                cacheOnly);
+    }
+
+    private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
+        if (contact.isSelf()) {
+            return get(contact.getAccount(), size, cachedOnly);
+        }
+        final String KEY = key(contact, size);
+        Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+        if (avatar != null || cachedOnly) {
+            return avatar;
+        }
+        if (contact.getAvatarFilename() != null && QuickConversationsService.isQuicksy()) {
+            avatar =
+                    mXmppConnectionService
+                            .getFileBackend()
+                            .getAvatar(contact.getAvatarFilename(), size);
+        }
+        if (avatar == null && contact.getProfilePhoto() != null) {
+            avatar =
+                    mXmppConnectionService
+                            .getFileBackend()
+                            .cropCenterSquare(Uri.parse(contact.getProfilePhoto()), size);
+        }
+        if (avatar == null && contact.getAvatarFilename() != null) {
+            avatar =
+                    mXmppConnectionService
+                            .getFileBackend()
+                            .getAvatar(contact.getAvatarFilename(), size);
+        }
+        if (avatar == null) {
+            avatar =
+                    get(
+                            contact.getDisplayName(),
+                            contact.getJid().asBareJid().toString(),
+                            size,
+                            false);
+        }
+        this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+        return avatar;
+    }
+
+    public Bitmap getRoundedShortcut(final MucOptions mucOptions) {
+        final DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
+        final int size = Math.round(metrics.density * 48);
+        final Bitmap bitmap = get(mucOptions, size, false);
+        final Bitmap output =
+                Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+        drawAvatar(bitmap, canvas, paint);
+        return output;
+    }
+
+    public Bitmap getRoundedShortcut(final Contact contact) {
+        return getRoundedShortcut(contact, false);
+    }
+
+    public Bitmap getRoundedShortcutWithIcon(final Contact contact) {
+        return getRoundedShortcut(contact, true);
+    }
+
+    private Bitmap getRoundedShortcut(final Contact contact, boolean withIcon) {
+        DisplayMetrics metrics = mXmppConnectionService.getResources().getDisplayMetrics();
+        int size = Math.round(metrics.density * 48);
+        Bitmap bitmap = get(contact, size);
+        Bitmap output =
+                Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+
+        drawAvatar(bitmap, canvas, paint);
+        if (withIcon) {
+            drawIcon(canvas, paint);
+        }
+        return output;
+    }
+
+    private static void drawAvatar(Bitmap bitmap, Canvas canvas, Paint paint) {
+        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        paint.setAntiAlias(true);
+        canvas.drawARGB(0, 0, 0, 0);
+        canvas.drawCircle(
+                bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        canvas.drawBitmap(bitmap, rect, rect, paint);
+    }
+
+    private void drawIcon(Canvas canvas, Paint paint) {
+        final Resources resources = mXmppConnectionService.getResources();
+        final Bitmap icon = getRoundLauncherIcon(resources);
+        if (icon == null) {
+            return;
+        }
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
+
+        int iconSize = Math.round(canvas.getHeight() / 2.6f);
+
+        int left = canvas.getWidth() - iconSize;
+        int top = canvas.getHeight() - iconSize;
+        final Rect rect = new Rect(left, top, left + iconSize, top + iconSize);
+        canvas.drawBitmap(icon, null, rect, paint);
+    }
+
+    private static Bitmap getRoundLauncherIcon(Resources resources) {
+
+        final Drawable drawable =
+                ResourcesCompat.getDrawable(resources, R.mipmap.new_launcher_round, null);
+        if (drawable == null) {
+            return null;
+        }
+
+        if (drawable instanceof BitmapDrawable) {
+            return ((BitmapDrawable) drawable).getBitmap();
+        }
+
+        Bitmap bitmap =
+                Bitmap.createBitmap(
+                        drawable.getIntrinsicWidth(),
+                        drawable.getIntrinsicHeight(),
+                        Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+
+        return bitmap;
+    }
+
+    public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) {
+        Contact c = user.getContact();
+        if (c != null
+                && (c.getProfilePhoto() != null
+                        || c.getAvatarFilename() != null
+                        || user.getAvatar() == null)) {
+            return get(c, size, cachedOnly);
+        } else {
+            return getImpl(user, size, cachedOnly);
+        }
+    }
+
+    private Bitmap getImpl(final MucOptions.User user, final int size, boolean cachedOnly) {
+        final String KEY = key(user, size);
+        Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
+        if (avatar != null || cachedOnly) {
+            return avatar;
+        }
+        if (user.getAvatar() != null) {
+            avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size);
+        }
+        if (avatar == null) {
+            Contact contact = user.getContact();
+            if (contact != null) {
+                avatar = get(contact, size, false);
+            } else {
+                String seed =
+                        user.getRealJid() != null ? user.getRealJid().asBareJid().toString() : null;
+                avatar = get(user.getName(), seed, size, false);
+            }
+        }
+        this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+        return avatar;
+    }
+
+    public void clear(Contact contact) {
+        synchronized (this.sizes) {
+            for (final Integer size : sizes) {
+                this.mXmppConnectionService.getBitmapCache().remove(key(contact, size));
+            }
+        }
+        for (Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
+            MucOptions.User user =
+                    conversation.getMucOptions().findUserByRealJid(contact.getJid().asBareJid());
+            if (user != null) {
+                clear(user);
+            }
+            clear(conversation);
+        }
+    }
+
+    private String key(Contact contact, int size) {
+        synchronized (this.sizes) {
+            this.sizes.add(size);
+        }
+        return PREFIX_CONTACT
+                + '\0'
+                + contact.getAccount().getJid().asBareJid()
+                + '\0'
+                + emptyOnNull(contact.getJid())
+                + '\0'
+                + size;
+    }
+
+    private String key(MucOptions.User user, int size) {
+        synchronized (this.sizes) {
+            this.sizes.add(size);
+        }
+        return PREFIX_CONTACT
+                + '\0'
+                + user.getAccount().getJid().asBareJid()
+                + '\0'
+                + emptyOnNull(user.getFullJid())
+                + '\0'
+                + emptyOnNull(user.getRealJid())
+                + '\0'
+                + size;
+    }
+
+    public Bitmap get(ListItem item, int size) {
+        return get(item, size, false);
+    }
+
+    public Bitmap get(ListItem item, int size, boolean cachedOnly) {
+        if (item instanceof RawBlockable) {
+            return get(item.getDisplayName(), item.getJid().toString(), size, cachedOnly);
+        } else if (item instanceof Contact) {
+            return get((Contact) item, size, cachedOnly);
+        } else if (item instanceof Bookmark bookmark) {
+            if (bookmark.getConversation() != null) {
+                return get(bookmark.getConversation(), size, cachedOnly);
+            } else {
+                Jid jid = bookmark.getJid();
+                Account account = bookmark.getAccount();
+                Contact contact = jid == null ? null : account.getRoster().getContact(jid);
+                if (contact != null && contact.getAvatarFilename() != null) {
+                    return get(contact, size, cachedOnly);
+                }
+                String seed = jid != null ? jid.asBareJid().toString() : null;
+                return get(bookmark.getDisplayName(), seed, size, cachedOnly);
+            }
+        } else {
+            String seed = item.getJid() != null ? item.getJid().asBareJid().toString() : null;
+            return get(item.getDisplayName(), seed, size, cachedOnly);
+        }
+    }
+
+    public Bitmap get(Conversation conversation, int size) {
+        return get(conversation, size, false);
+    }
+
+    public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
+        if (conversation.getMode() == Conversation.MODE_SINGLE) {
+            return get(conversation.getContact(), size, cachedOnly);
+        } else {
+            return get(conversation.getMucOptions(), size, cachedOnly);
+        }
+    }
+
+    public void clear(Conversation conversation) {
+        if (conversation.getMode() == Conversation.MODE_SINGLE) {
+            clear(conversation.getContact());
+        } else {
+            clear(conversation.getMucOptions());
+            synchronized (this.conversationDependentKeys) {
+                Set<String> keys = this.conversationDependentKeys.get(conversation.getUuid());
+                if (keys == null) {
+                    return;
+                }
+                LruCache<String, Bitmap> cache = this.mXmppConnectionService.getBitmapCache();
+                for (String key : keys) {
+                    cache.remove(key);
+                }
+                keys.clear();
+            }
+        }
+    }
+
+    private Bitmap get(MucOptions mucOptions, int size, boolean cachedOnly) {
+        final String KEY = key(mucOptions, size);
+        Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
+        if (bitmap != null || cachedOnly) {
+            return bitmap;
+        }
+
+        bitmap = mXmppConnectionService.getFileBackend().getAvatar(mucOptions.getAvatar(), size);
+
+        if (bitmap == null) {
+            Conversation c = mucOptions.getConversation();
+            if (mucOptions.isPrivateAndNonAnonymous()) {
+                final List<MucOptions.User> users = mucOptions.getUsersRelevantForNameAndAvatar();
+                if (users.size() == 0) {
+                    bitmap =
+                            getImpl(
+                                    c.getName().toString(),
+                                    c.getJid().asBareJid().toString(),
+                                    size);
+                } else {
+                    bitmap = getImpl(users, size);
+                }
+            } else {
+                bitmap = getImpl(CHANNEL_SYMBOL, c.getJid().asBareJid().toString(), size);
+            }
+        }
+
+        this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+
+        return bitmap;
+    }
+
+    private Bitmap get(List<MucOptions.User> users, int size, boolean cachedOnly) {
+        final String KEY = key(users, size);
+        Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
+        if (bitmap != null || cachedOnly) {
+            return bitmap;
+        }
+        bitmap = getImpl(users, size);
+        this.mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+        return bitmap;
+    }
+
+    private Bitmap getImpl(List<MucOptions.User> users, int size) {
+        int count = users.size();
+        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        bitmap.eraseColor(TRANSPARENT);
+        if (count == 0) {
+            throw new AssertionError("Unable to draw tiles for 0 users");
+        } else if (count == 1) {
+            drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+            drawTile(canvas, users.get(0).getAccount(), size / 2 + 1, 0, size, size);
+        } else if (count == 2) {
+            drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+            drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size);
+        } else if (count == 3) {
+            drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size);
+            drawTile(canvas, users.get(1), size / 2 + 1, 0, size, size / 2 - 1);
+            drawTile(canvas, users.get(2), size / 2 + 1, size / 2 + 1, size, size);
+        } else if (count == 4) {
+            drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+            drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+            drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+            drawTile(canvas, users.get(3), size / 2 + 1, size / 2 + 1, size, size);
+        } else {
+            drawTile(canvas, users.get(0), 0, 0, size / 2 - 1, size / 2 - 1);
+            drawTile(canvas, users.get(1), 0, size / 2 + 1, size / 2 - 1, size);
+            drawTile(canvas, users.get(2), size / 2 + 1, 0, size, size / 2 - 1);
+            drawTile(canvas, "\u2026", PLACEHOLDER_COLOR, size / 2 + 1, size / 2 + 1, size, size);
+        }
+        return bitmap;
+    }
+
+    public void clear(final MucOptions options) {
+        if (options == null) {
+            return;
+        }
+        synchronized (this.sizes) {
+            for (Integer size : sizes) {
+                this.mXmppConnectionService.getBitmapCache().remove(key(options, size));
+            }
+        }
+    }
+
+    private String key(final MucOptions options, int size) {
+        synchronized (this.sizes) {
+            this.sizes.add(size);
+        }
+        return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size;
+    }
+
+    private String key(List<MucOptions.User> users, int size) {
+        final Conversation conversation = users.get(0).getConversation();
+        StringBuilder builder = new StringBuilder("TILE_");
+        builder.append(conversation.getUuid());
+
+        for (MucOptions.User user : users) {
+            builder.append("\0");
+            builder.append(emptyOnNull(user.getRealJid()));
+            builder.append("\0");
+            builder.append(emptyOnNull(user.getFullJid()));
+        }
+        builder.append('\0');
+        builder.append(size);
+        final String key = builder.toString();
+        synchronized (this.conversationDependentKeys) {
+            Set<String> keys;
+            if (this.conversationDependentKeys.containsKey(conversation.getUuid())) {
+                keys = this.conversationDependentKeys.get(conversation.getUuid());
+            } else {
+                keys = new HashSet<>();
+                this.conversationDependentKeys.put(conversation.getUuid(), keys);
+            }
+            keys.add(key);
+        }
+        return key;
+    }
+
+    public Bitmap get(Account account, int size) {
+        return get(account, size, false);
+    }
+
+    public Bitmap get(Account account, int size, boolean cachedOnly) {
+        final String KEY = key(account, size);
+        Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
+        if (avatar != null || cachedOnly) {
+            return avatar;
+        }
+        avatar = mXmppConnectionService.getFileBackend().getAvatar(account.getAvatar(), size);
+        if (avatar == null) {
+            final String displayName = account.getDisplayName();
+            final String jid = account.getJid().asBareJid().toString();
+            if (QuickConversationsService.isQuicksy() && !TextUtils.isEmpty(displayName)) {
+                avatar = get(displayName, jid, size, false);
+            } else {
+                avatar = get(jid, null, size, false);
+            }
+        }
+        mXmppConnectionService.getBitmapCache().put(KEY, avatar);
+        return avatar;
+    }
+
+    public Bitmap get(Message message, int size, boolean cachedOnly) {
+        final Conversational conversation = message.getConversation();
+        if (message.getType() == Message.TYPE_STATUS
+                && message.getCounterparts() != null
+                && message.getCounterparts().size() > 1) {
+            return get(message.getCounterparts(), size, cachedOnly);
+        } else if (message.getStatus() == Message.STATUS_RECEIVED) {
+            Contact c = message.getContact();
+            if (c != null && (c.getProfilePhoto() != null || c.getAvatarFilename() != null)) {
+                return get(c, size, cachedOnly);
+            } else if (conversation instanceof Conversation
+                    && message.getConversation().getMode() == Conversation.MODE_MULTI) {
+                final Jid trueCounterpart = message.getTrueCounterpart();
+                final MucOptions mucOptions = ((Conversation) conversation).getMucOptions();
+                MucOptions.User user;
+                if (trueCounterpart != null) {
+                    user =
+                            mucOptions.findOrCreateUserByRealJid(
+                                    trueCounterpart, message.getCounterpart());
+                } else {
+                    user = mucOptions.findUserByFullJid(message.getCounterpart());
+                }
+                if (user != null) {
+                    return getImpl(user, size, cachedOnly);
+                }
+            } else if (c != null) {
+                return get(c, size, cachedOnly);
+            }
+            Jid tcp = message.getTrueCounterpart();
+            String seed = tcp != null ? tcp.asBareJid().toString() : null;
+            return get(UIHelper.getMessageDisplayName(message), seed, size, cachedOnly);
+        } else {
+            return get(conversation.getAccount(), size, cachedOnly);
+        }
+    }
+
+    public void clear(Account account) {
+        synchronized (this.sizes) {
+            for (Integer size : sizes) {
+                this.mXmppConnectionService.getBitmapCache().remove(key(account, size));
+            }
+        }
+    }
+
+    public void clear(MucOptions.User user) {
+        synchronized (this.sizes) {
+            for (Integer size : sizes) {
+                this.mXmppConnectionService.getBitmapCache().remove(key(user, size));
+            }
+        }
+    }
+
+    private String key(Account account, int size) {
+        synchronized (this.sizes) {
+            this.sizes.add(size);
+        }
+        return PREFIX_ACCOUNT + "_" + account.getUuid() + "_" + size;
+    }
+
+    /*public Bitmap get(String name, int size) {
+    	return get(name,null, size,false);
+    }*/
+
+    public Bitmap get(final String name, String seed, final int size, boolean cachedOnly) {
+        final String KEY = key(seed == null ? name : name + "\0" + seed, size);
+        Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
+        if (bitmap != null || cachedOnly) {
+            return bitmap;
+        }
+        bitmap = getImpl(name, seed, size);
+        mXmppConnectionService.getBitmapCache().put(KEY, bitmap);
+        return bitmap;
+    }
+
+    public static Bitmap get(final Jid jid, final int size) {
+        return getImpl(jid.asBareJid().toString(), null, size);
+    }
+
+    private static Bitmap getImpl(final String name, final String seed, final int size) {
+        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        final String trimmedName = name == null ? "" : name.trim();
+        drawTile(canvas, trimmedName, seed, 0, 0, size, size);
+        return bitmap;
+    }
+
+    private String key(String name, int size) {
+        synchronized (this.sizes) {
+            this.sizes.add(size);
+        }
+        return PREFIX_GENERIC + "_" + name + "_" + size;
+    }
+
+    private static boolean drawTile(
+            Canvas canvas, String letter, int tileColor, int left, int top, int right, int bottom) {
+        letter = letter.toUpperCase(Locale.getDefault());
+        Paint tilePaint = new Paint(), textPaint = new Paint();
+        tilePaint.setColor(tileColor);
+        textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+        textPaint.setColor(FG_COLOR);
+        textPaint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
+        textPaint.setTextSize((float) ((right - left) * 0.8));
+        Rect rect = new Rect();
+
+        canvas.drawRect(new Rect(left, top, right, bottom), tilePaint);
+        textPaint.getTextBounds(letter, 0, 1, rect);
+        float width = textPaint.measureText(letter);
+        canvas.drawText(
+                letter,
+                (right + left) / 2 - width / 2,
+                (top + bottom) / 2 + rect.height() / 2,
+                textPaint);
+        return true;
+    }
+
+    private boolean drawTile(
+            Canvas canvas, MucOptions.User user, int left, int top, int right, int bottom) {
+        Contact contact = user.getContact();
+        if (contact != null) {
+            Uri uri = null;
+            if (contact.getAvatarFilename() != null && QuickConversationsService.isQuicksy()) {
+                uri =
+                        mXmppConnectionService
+                                .getFileBackend()
+                                .getAvatarUri(contact.getAvatarFilename());
+            } else if (contact.getProfilePhoto() != null) {
+                uri = Uri.parse(contact.getProfilePhoto());
+            } else if (contact.getAvatarFilename() != null) {
+                uri =
+                        mXmppConnectionService
+                                .getFileBackend()
+                                .getAvatarUri(contact.getAvatarFilename());
+            }
+            if (drawTile(canvas, uri, left, top, right, bottom)) {
+                return true;
+            }
+        } else if (user.getAvatar() != null) {
+            Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar());
+            if (drawTile(canvas, uri, left, top, right, bottom)) {
+                return true;
+            }
+        }
+        if (contact != null) {
+            String seed = contact.getJid().asBareJid().toString();
+            drawTile(canvas, contact.getDisplayName(), seed, left, top, right, bottom);
+        } else {
+            String seed =
+                    user.getRealJid() == null ? null : user.getRealJid().asBareJid().toString();
+            drawTile(canvas, user.getName(), seed, left, top, right, bottom);
+        }
+        return true;
+    }
+
+    private boolean drawTile(
+            Canvas canvas, Account account, int left, int top, int right, int bottom) {
+        String avatar = account.getAvatar();
+        if (avatar != null) {
+            Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(avatar);
+            if (uri != null) {
+                if (drawTile(canvas, uri, left, top, right, bottom)) {
+                    return true;
+                }
+            }
+        }
+        String name = account.getJid().asBareJid().toString();
+        return drawTile(canvas, name, name, left, top, right, bottom);
+    }
+
+    private static boolean drawTile(
+            Canvas canvas, String name, String seed, int left, int top, int right, int bottom) {
+        if (name != null) {
+            final String letter = name.equals(CHANNEL_SYMBOL) ? name : getFirstLetter(name);
+            final int color = UIHelper.getColorForName(seed == null ? name : seed);
+            drawTile(canvas, letter, color, left, top, right, bottom);
+            return true;
+        }
+        return false;
+    }
+
+    private static String getFirstLetter(String name) {
+        for (Character c : name.toCharArray()) {
+            if (Character.isLetterOrDigit(c)) {
+                return c.toString();
+            }
+        }
+        return "X";
+    }
+
+    private boolean drawTile(Canvas canvas, Uri uri, int left, int top, int right, int bottom) {
+        if (uri != null) {
+            Bitmap bitmap =
+                    mXmppConnectionService
+                            .getFileBackend()
+                            .cropCenter(uri, bottom - top, right - left);
+            if (bitmap != null) {
+                drawTile(canvas, bitmap, left, top, right, bottom);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean drawTile(
+            Canvas canvas, Bitmap bm, int dstleft, int dsttop, int dstright, int dstbottom) {
+        Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
+        canvas.drawBitmap(bm, null, dst, null);
+        return true;
+    }
+
+    @Override
+    public void onAdvancedStreamFeaturesAvailable(Account account) {
+        XmppConnection.Features features = account.getXmppConnection().getFeatures();
+        if (features.pep() && !features.pepPersistent()) {
+            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": has pep but is not persistent");
+            if (account.getAvatar() != null) {
+                mXmppConnectionService.republishAvatarIfNeeded(account);
+            }
+        }
+    }
+
+    private static String emptyOnNull(@Nullable Jid value) {
+        return value == null ? "" : value.toString();
+    }
+
+    public interface Avatarable {
+        @ColorInt
+        int getAvatarBackgroundColor();
+
+        String getAvatarName();
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegration.java b/src/main/java/eu/siacs/conversations/services/CallIntegration.java
index f4d4fae35..747f79fb3 100644
--- a/src/main/java/eu/siacs/conversations/services/CallIntegration.java
+++ b/src/main/java/eu/siacs/conversations/services/CallIntegration.java
@@ -455,7 +455,7 @@ public class CallIntegration extends Connection {
     }
 
     public static Uri address(final Jid contact) {
-        return Uri.parse(String.format("xmpp:%s", contact.toEscapedString()));
+        return Uri.parse(String.format("xmpp:%s", contact.toString()));
     }
 
     public void verifyDisconnected() {
diff --git a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java
index a225dcbee..359504396 100644
--- a/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java
@@ -20,14 +20,11 @@ import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.util.Log;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.core.content.ContextCompat;
-
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
@@ -40,7 +37,6 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
 import eu.siacs.conversations.xmpp.jingle.Media;
 import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -91,7 +87,8 @@ public class CallIntegrationConnectionService extends ConnectionService {
         if (service == null) {
             Log.d(
                     Config.LOGTAG,
-                    "CallIntegrationConnection service was unable to bind to XmppConnectionService");
+                    "CallIntegrationConnection service was unable to bind to"
+                            + " XmppConnectionService");
             return Connection.createFailedConnection(
                     new DisconnectCause(DisconnectCause.ERROR, "service connection not found"));
         }
@@ -107,8 +104,8 @@ public class CallIntegrationConnectionService extends ConnectionService {
         Log.d(Config.LOGTAG, "create outgoing rtp connection!");
         final Intent intent = new Intent(service, RtpSessionActivity.class);
         intent.setAction(Intent.ACTION_VIEW);
-        intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString());
-        intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString());
+        intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toString());
+        intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toString());
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
         final Connection callIntegration;
@@ -135,7 +132,8 @@ public class CallIntegrationConnectionService extends ConnectionService {
                     return Connection.createFailedConnection(
                             new DisconnectCause(
                                     DisconnectCause.ERROR,
-                                    "Phone is busy. Probably race condition. Try again in a moment"));
+                                    "Phone is busy. Probably race condition. Try again in a"
+                                            + " moment"));
                 }
                 if (proposal == null) {
                     // TODO instead of just null checking try to get the sessionID
@@ -187,9 +185,9 @@ public class CallIntegrationConnectionService extends ConnectionService {
         }
         final Jid jid;
         if ("tel".equals(uri.getScheme())) {
-            jid = Jid.ofEscaped(extras.getString(EXTRA_ADDRESS));
+            jid = Jid.of(extras.getString(EXTRA_ADDRESS));
         } else {
-            jid = Jid.ofEscaped(uri.getSchemeSpecificPart());
+            jid = Jid.of(uri.getSchemeSpecificPart());
         }
         final int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE);
         final Set<Media> media =
@@ -226,7 +224,7 @@ public class CallIntegrationConnectionService extends ConnectionService {
             return Connection.createFailedConnection(
                     new DisconnectCause(DisconnectCause.ERROR, "service connection not found"));
         }
-        final var jid = Jid.ofEscaped(uri.getSchemeSpecificPart());
+        final var jid = Jid.of(uri.getSchemeSpecificPart());
         final Account account = service.findAccountByUuid(phoneAccountHandle.getId());
         final var weakReference =
                 service.getJingleConnectionManager().findJingleRtpConnection(account, jid, sid);
@@ -365,7 +363,7 @@ public class CallIntegrationConnectionService extends ConnectionService {
             } else {
                 // for Android 8 we need to put in a fake tel uri
                 final var outgoingCallExtras = new Bundle();
-                outgoingCallExtras.putString(EXTRA_ADDRESS, with.toEscapedString());
+                outgoingCallExtras.putString(EXTRA_ADDRESS, with.toString());
                 extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, outgoingCallExtras);
                 address = Uri.parse("tel:0");
             }
diff --git a/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java
index 5a8c58ac6..9e09e56e9 100644
--- a/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java
+++ b/src/main/java/eu/siacs/conversations/services/ChannelDiscoveryService.java
@@ -1,14 +1,10 @@
 package eu.siacs.conversations.services;
 
-
 import android.util.Log;
-
 import androidx.annotation.NonNull;
-
 import com.google.common.base.Strings;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Room;
@@ -17,18 +13,7 @@ import eu.siacs.conversations.http.services.MuclumbusService;
 import eu.siacs.conversations.parser.IqParser;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.XmppConnection;
-
 import im.conversations.android.xmpp.model.stanza.Iq;
-
-import okhttp3.OkHttpClient;
-import okhttp3.ResponseBody;
-
-import retrofit2.Call;
-import retrofit2.Callback;
-import retrofit2.Response;
-import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -38,6 +23,13 @@ import java.util.Map;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+import okhttp3.OkHttpClient;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
 
 public class ChannelDiscoveryService {
 
@@ -57,7 +49,8 @@ public class ChannelDiscoveryService {
             this.muclumbusService = null;
             return;
         }
-        final OkHttpClient.Builder builder = HttpConnectionManager.okHttpClient(service).newBuilder();
+        final OkHttpClient.Builder builder =
+                HttpConnectionManager.okHttpClient(service).newBuilder();
         if (service.useTorToConnect()) {
             builder.proxy(HttpConnectionManager.getProxy());
         }
@@ -205,10 +198,8 @@ public class ChannelDiscoveryService {
                                         account,
                                         infoRequest,
                                         infoResponse -> {
-                                            if (infoResponse.getType()
-                                                    == Iq.Type.RESULT) {
-                                                final Room room =
-                                                        IqParser.parseRoom(infoResponse);
+                                            if (infoResponse.getType() == Iq.Type.RESULT) {
+                                                final Room room = IqParser.parseRoom(infoResponse);
                                                 if (room != null) {
                                                     rooms.add(room);
                                                 }
@@ -260,7 +251,7 @@ public class ChannelDiscoveryService {
                     continue;
                 }
                 for (final String mucService : xmppConnection.getMucServers()) {
-                    Jid jid = Jid.ofEscaped(mucService);
+                    final Jid jid = Jid.of(mucService);
                     if (!localMucServices.containsKey(jid)) {
                         localMucServices.put(jid, account);
                     }
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index 83bef27e7..cd2045774 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -573,9 +573,8 @@ public class NotificationService {
         final Intent fullScreenIntent =
                 new Intent(mXmppConnectionService, RtpSessionActivity.class);
         fullScreenIntent.putExtra(
-                RtpSessionActivity.EXTRA_ACCOUNT,
-                id.account.getJid().asBareJid().toEscapedString());
-        fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
+                RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toString());
+        fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
         fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
         fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -702,9 +701,8 @@ public class NotificationService {
                 new Intent(mXmppConnectionService, RtpSessionActivity.class);
         fullScreenIntent.setAction(action);
         fullScreenIntent.putExtra(
-                RtpSessionActivity.EXTRA_ACCOUNT,
-                id.account.getJid().asBareJid().toEscapedString());
-        fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
+                RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toString());
+        fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
         fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
         return PendingIntent.getActivity(
                 mXmppConnectionService,
@@ -1902,7 +1900,7 @@ public class NotificationService {
         } else if (errors.size() == 1) {
             mBuilder.setContentTitle(
                     mXmppConnectionService.getString(R.string.problem_connecting_to_account));
-            mBuilder.setContentText(errors.get(0).getJid().asBareJid().toEscapedString());
+            mBuilder.setContentText(errors.get(0).getJid().asBareJid().toString());
         } else {
             mBuilder.setContentTitle(
                     mXmppConnectionService.getString(R.string.problem_connecting_to_accounts));
@@ -1961,7 +1959,7 @@ public class NotificationService {
             intent = new Intent(mXmppConnectionService, AccountUtils.MANAGE_ACCOUNT_ACTIVITY);
         } else {
             intent = new Intent(mXmppConnectionService, EditAccountActivity.class);
-            intent.putExtra("jid", errors.get(0).getJid().asBareJid().toEscapedString());
+            intent.putExtra("jid", errors.get(0).getJid().asBareJid().toString());
             intent.putExtra(EditAccountActivity.EXTRA_OPENED_FROM_NOTIFICATION, true);
         }
         mBuilder.setContentIntent(
diff --git a/src/main/java/eu/siacs/conversations/services/ShortcutService.java b/src/main/java/eu/siacs/conversations/services/ShortcutService.java
index b2a3cce75..132f70912 100644
--- a/src/main/java/eu/siacs/conversations/services/ShortcutService.java
+++ b/src/main/java/eu/siacs/conversations/services/ShortcutService.java
@@ -159,17 +159,15 @@ public class ShortcutService {
     }
 
     private static String getShortcutId(final Contact contact) {
-        return contact.getAccount().getJid().asBareJid().toEscapedString()
+        return contact.getAccount().getJid().asBareJid().toString()
                 + "#"
-                + contact.getJid().asBareJid().toEscapedString();
+                + contact.getJid().asBareJid().toString();
     }
 
     private static String getShortcutId(final MucOptions mucOptions) {
         final Account account = mucOptions.getAccount();
         final Jid jid = mucOptions.getConversation().getJid();
-        return account.getJid().asBareJid().toEscapedString()
-                + "#"
-                + jid.asBareJid().toEscapedString();
+        return account.getJid().asBareJid().toString() + "#" + jid.asBareJid().toString();
     }
 
     private Intent getShortcutIntent(final MucOptions mucOptions) {
@@ -179,17 +177,12 @@ public class ShortcutService {
                 Uri.parse(
                         String.format(
                                 "xmpp:%s?join",
-                                mucOptions
-                                        .getConversation()
-                                        .getJid()
-                                        .asBareJid()
-                                        .toEscapedString())));
+                                mucOptions.getConversation().getJid().asBareJid().toString())));
     }
 
     private Intent getShortcutIntent(final Contact contact) {
         return getShortcutIntent(
-                contact.getAccount(),
-                Uri.parse("xmpp:" + contact.getJid().asBareJid().toEscapedString()));
+                contact.getAccount(), Uri.parse("xmpp:" + contact.getJid().asBareJid().toString()));
     }
 
     private Intent getShortcutIntent(final Account account, final Uri uri) {
diff --git a/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java b/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java
index 92ae9d9ec..24aaf1f42 100644
--- a/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java
+++ b/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java
@@ -10,10 +10,8 @@ import android.os.Messenger;
 import android.os.RemoteException;
 import android.preference.PreferenceManager;
 import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
@@ -22,7 +20,6 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
@@ -34,7 +31,6 @@ import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.xmpp.Jid;
 import im.conversations.android.xmpp.model.stanza.Iq;
 import im.conversations.android.xmpp.model.stanza.Presence;
-
 import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.util.List;
@@ -92,7 +88,8 @@ public class UnifiedPushBroker {
         renewUnifiedPushEndpoints(null);
     }
 
-    public Optional<Transport> renewUnifiedPushEndpoints(@Nullable final PushTargetMessenger pushTargetMessenger) {
+    public Optional<Transport> renewUnifiedPushEndpoints(
+            @Nullable final PushTargetMessenger pushTargetMessenger) {
         final Optional<Transport> transportOptional = getTransport();
         if (transportOptional.isPresent()) {
             final Transport transport = transportOptional.get();
@@ -100,13 +97,13 @@ public class UnifiedPushBroker {
                 renewUnifiedEndpoint(transportOptional.get(), pushTargetMessenger);
             } else {
                 if (pushTargetMessenger != null && pushTargetMessenger.messenger != null) {
-                    sendRegistrationDelayed(pushTargetMessenger.messenger,"account is disabled");
+                    sendRegistrationDelayed(pushTargetMessenger.messenger, "account is disabled");
                 }
                 Log.d(Config.LOGTAG, "skipping UnifiedPush endpoint renewal. Account is disabled");
             }
         } else {
             if (pushTargetMessenger != null && pushTargetMessenger.messenger != null) {
-                sendRegistrationDelayed(pushTargetMessenger.messenger,"no transport selected");
+                sendRegistrationDelayed(pushTargetMessenger.messenger, "no transport selected");
             }
             Log.d(Config.LOGTAG, "skipping UnifiedPush endpoint renewal. No transport selected");
         }
@@ -121,16 +118,16 @@ public class UnifiedPushBroker {
         try {
             messenger.send(message);
         } catch (final RemoteException e) {
-            Log.d(Config.LOGTAG,"unable to tell messenger of delayed registration",e);
+            Log.d(Config.LOGTAG, "unable to tell messenger of delayed registration", e);
         }
     }
 
-    private void renewUnifiedEndpoint(final Transport transport, final PushTargetMessenger pushTargetMessenger) {
+    private void renewUnifiedEndpoint(
+            final Transport transport, final PushTargetMessenger pushTargetMessenger) {
         final Account account = transport.account;
         final UnifiedPushDatabase unifiedPushDatabase = UnifiedPushDatabase.getInstance(service);
         final List<UnifiedPushDatabase.PushTarget> renewals =
-                unifiedPushDatabase.getRenewals(
-                        account.getUuid(), transport.transport.toEscapedString());
+                unifiedPushDatabase.getRenewals(account.getUuid(), transport.transport.toString());
         Log.d(
                 Config.LOGTAG,
                 account.getJid().asBareJid()
@@ -142,7 +139,11 @@ public class UnifiedPushBroker {
             Log.d(
                     Config.LOGTAG,
                     account.getJid().asBareJid() + ": try to renew UnifiedPush " + renewal);
-            UnifiedPushDistributor.quickLog(service,String.format("%s: try to renew UnifiedPush %s", account.getJid(), renewal.toString()));
+            UnifiedPushDistributor.quickLog(
+                    service,
+                    String.format(
+                            "%s: try to renew UnifiedPush %s",
+                            account.getJid(), renewal.toString()));
             final String hashedApplication =
                     UnifiedPushDistributor.hash(account.getUuid(), renewal.application);
             final String hashedInstance =
@@ -205,7 +206,7 @@ public class UnifiedPushBroker {
                 unifiedPushDatabase.updateEndpoint(
                         renewal.instance,
                         transport.account.getUuid(),
-                        transport.transport.toEscapedString(),
+                        transport.transport.toString(),
                         endpoint,
                         expiration);
         if (modified) {
@@ -231,15 +232,21 @@ public class UnifiedPushBroker {
         }
     }
 
-    private void sendEndpoint(final Messenger messenger, String instance, final UnifiedPushDatabase.ApplicationEndpoint applicationEndpoint) {
+    private void sendEndpoint(
+            final Messenger messenger,
+            String instance,
+            final UnifiedPushDatabase.ApplicationEndpoint applicationEndpoint) {
         if (messenger != null) {
-            Log.d(Config.LOGTAG,"using messenger instead of broadcast to communicate endpoint to "+applicationEndpoint.application);
+            Log.d(
+                    Config.LOGTAG,
+                    "using messenger instead of broadcast to communicate endpoint to "
+                            + applicationEndpoint.application);
             final Message message = new Message();
             message.obj = endpointIntent(instance, applicationEndpoint);
             try {
                 messenger.send(message);
             } catch (final RemoteException e) {
-                Log.d(Config.LOGTAG,"messenger failed. falling back to broadcast");
+                Log.d(Config.LOGTAG, "messenger failed. falling back to broadcast");
                 broadcastEndpoint(instance, applicationEndpoint);
             }
         } else {
@@ -281,8 +288,7 @@ public class UnifiedPushBroker {
                 future,
                 new FutureCallback<>() {
                     @Override
-                    public void onSuccess(
-                            final List<UnifiedPushDatabase.PushTarget> pushTargets) {
+                    public void onSuccess(final List<UnifiedPushDatabase.PushTarget> pushTargets) {
                         broadcastUnregistered(pushTargets);
                     }
 
@@ -290,19 +296,21 @@ public class UnifiedPushBroker {
                     public void onFailure(@NonNull Throwable throwable) {
                         Log.d(
                                 Config.LOGTAG,
-                                "could not delete endpoints after UnifiedPushDistributor was disabled");
+                                "could not delete endpoints after UnifiedPushDistributor was"
+                                        + " disabled");
                     }
                 },
                 MoreExecutors.directExecutor());
     }
 
     private ListenableFuture<List<UnifiedPushDatabase.PushTarget>> deletePushTargets() {
-        return Futures.submit(() -> UnifiedPushDatabase.getInstance(service).deletePushTargets(),SCHEDULER);
+        return Futures.submit(
+                () -> UnifiedPushDatabase.getInstance(service).deletePushTargets(), SCHEDULER);
     }
 
     private void broadcastUnregistered(final List<UnifiedPushDatabase.PushTarget> pushTargets) {
-        for(final UnifiedPushDatabase.PushTarget pushTarget : pushTargets) {
-            Log.d(Config.LOGTAG,"sending unregistered to "+pushTarget);
+        for (final UnifiedPushDatabase.PushTarget pushTarget : pushTargets) {
+            Log.d(Config.LOGTAG, "sending unregistered to " + pushTarget);
             broadcastUnregistered(pushTarget);
         }
     }
@@ -368,8 +376,8 @@ public class UnifiedPushBroker {
         final Jid transport;
         final Jid jid;
         try {
-            transport = Jid.ofEscaped(Strings.nullToEmpty(pushServerPreference).trim());
-            jid = Jid.ofEscaped(Strings.nullToEmpty(accountPreference).trim());
+            transport = Jid.of(Strings.nullToEmpty(pushServerPreference).trim());
+            jid = Jid.of(Strings.nullToEmpty(accountPreference).trim());
         } catch (final IllegalArgumentException e) {
             return Optional.absent();
         }
@@ -390,8 +398,7 @@ public class UnifiedPushBroker {
         }
         final String uuid = account.getUuid();
         final List<UnifiedPushDatabase.PushTarget> pushTargets =
-                UnifiedPushDatabase.getInstance(service)
-                        .getPushTargets(uuid, transport.toEscapedString());
+                UnifiedPushDatabase.getInstance(service).getPushTargets(uuid, transport.toString());
         return Iterables.tryFind(
                 pushTargets,
                 pt ->
@@ -422,7 +429,8 @@ public class UnifiedPushBroker {
         service.sendBroadcast(updateIntent);
     }
 
-    private Intent endpointIntent(final String instance, final UnifiedPushDatabase.ApplicationEndpoint endpoint) {
+    private Intent endpointIntent(
+            final String instance, final UnifiedPushDatabase.ApplicationEndpoint endpoint) {
         final Intent intent = new Intent(UnifiedPushDistributor.ACTION_NEW_ENDPOINT);
         intent.setPackage(endpoint.application);
         intent.putExtra("token", instance);
@@ -449,13 +457,12 @@ public class UnifiedPushBroker {
         return intent;
     }
 
-    public void rebroadcastEndpoint(final Messenger messenger, final String instance, final Transport transport) {
+    public void rebroadcastEndpoint(
+            final Messenger messenger, final String instance, final Transport transport) {
         final UnifiedPushDatabase unifiedPushDatabase = UnifiedPushDatabase.getInstance(service);
         final UnifiedPushDatabase.ApplicationEndpoint endpoint =
                 unifiedPushDatabase.getEndpoint(
-                        transport.account.getUuid(),
-                        transport.transport.toEscapedString(),
-                        instance);
+                        transport.account.getUuid(), transport.transport.toString(), instance);
         if (endpoint != null) {
             sendEndpoint(messenger, instance, endpoint);
         }
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index c9c116c3a..9a5ea426f 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -120,7 +120,6 @@ import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.LocalizedContent;
 import eu.siacs.conversations.xml.Namespace;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnContactStatusChanged;
 import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
@@ -2069,7 +2068,7 @@ public class XmppConnectionService extends Service {
                             if (uri != null) {
                                 final EasyOnboardingInvite invite =
                                         new EasyOnboardingInvite(
-                                                jid.getDomain().toEscapedString(), uri, landingUrl);
+                                                jid.getDomain().toString(), uri, landingUrl);
                                 callback.inviteRequested(invite);
                                 return;
                             }
@@ -2145,7 +2144,7 @@ public class XmppConnectionService extends Service {
 
     public void processMdsItem(final Account account, final Element item) {
         final Jid jid =
-                item == null ? null : InvalidJid.getNullForInvalid(item.getAttributeAsJid("id"));
+                item == null ? null : Jid.Invalid.getNullForInvalid(item.getAttributeAsJid("id"));
         if (jid == null) {
             return;
         }
@@ -2306,7 +2305,7 @@ public class XmppConnectionService extends Service {
                     account,
                     Namespace.BOOKMARKS2,
                     item,
-                    bookmark.getJid().asBareJid().toEscapedString(),
+                    bookmark.getJid().asBareJid().toString(),
                     PublishOptions.persistentWhitelistAccessMaxItems());
         } else if (connection.getFeatures().bookmarksConversion()) {
             pushBookmarksPep(account);
@@ -2321,7 +2320,7 @@ public class XmppConnectionService extends Service {
         if (connection.getFeatures().bookmarks2()) {
             final Iq request =
                     mIqGenerator.deleteItem(
-                            Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString());
+                            Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toString());
             Log.d(
                     Config.LOGTAG,
                     account.getJid().asBareJid() + ": removing bookmark via Bookmarks 2");
@@ -3027,10 +3026,10 @@ public class XmppConnectionService extends Service {
     }
 
     private void provisionAccount(final String address, final String password) {
-        final Jid jid = Jid.ofEscaped(address);
+        final Jid jid = Jid.of(address);
         final Account account = new Account(jid, password);
         account.setOption(Account.OPTION_DISABLED, true);
-        Log.d(Config.LOGTAG, jid.asBareJid().toEscapedString() + ": provisioning account");
+        Log.d(Config.LOGTAG, jid.asBareJid().toString() + ": provisioning account");
         createAccount(account);
     }
 
@@ -5704,7 +5703,7 @@ public class XmppConnectionService extends Service {
                 account,
                 Namespace.MDS_DISPLAYED,
                 item,
-                itemId.toEscapedString(),
+                itemId.toString(),
                 PublishOptions.persistentWhitelistAccessMaxItems());
     }
 
@@ -5819,7 +5818,7 @@ public class XmppConnectionService extends Service {
         if (Config.QUICKSY_DOMAIN != null) {
             hosts.remove(
                     Config.QUICKSY_DOMAIN
-                            .toEscapedString()); // we only want to show this when we type a e164
+                            .toString()); // we only want to show this when we type a e164
             // number
         }
         if (Config.MAGIC_CREATE_DOMAIN != null) {
@@ -5835,7 +5834,7 @@ public class XmppConnectionService extends Service {
                 mucServers.addAll(account.getXmppConnection().getMucServers());
                 for (final Bookmark bookmark : account.getBookmarks()) {
                     final Jid jid = bookmark.getJid();
-                    final String s = jid == null ? null : jid.getDomain().toEscapedString();
+                    final String s = jid == null ? null : jid.getDomain().toString();
                     if (s != null) {
                         mucServers.add(s);
                     }
diff --git a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
index 04678c3c7..195ca74f2 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
@@ -2,13 +2,9 @@ package eu.siacs.conversations.ui;
 
 import android.view.View;
 import android.widget.Toast;
-
 import androidx.annotation.StringRes;
-import androidx.appcompat.app.AlertDialog;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.DialogBlockContactBinding;
 import eu.siacs.conversations.entities.Blockable;
@@ -17,43 +13,56 @@ import eu.siacs.conversations.ui.util.JidDialog;
 
 public final class BlockContactDialog {
 
-	public static void show(final XmppActivity xmppActivity, final Blockable blockable) {
-		show(xmppActivity, blockable, null);
-	}
-	public static void show(final XmppActivity xmppActivity, final Blockable blockable, final String serverMsgId) {
-		final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(xmppActivity);
-		final boolean isBlocked = blockable.isBlocked();
-		builder.setNegativeButton(R.string.cancel, null);
-		DialogBlockContactBinding binding = DataBindingUtil.inflate(xmppActivity.getLayoutInflater(), R.layout.dialog_block_contact, null, false);
-		final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
-		if (reporting && !isBlocked) {
-			binding.reportSpam.setVisibility(View.VISIBLE);
-			if (serverMsgId != null) {
-				binding.reportSpam.setChecked(true);
-				binding.reportSpam.setEnabled(false);
-			} else {
-				binding.reportSpam.setEnabled(true);
-			}
-		} else {
-			binding.reportSpam.setVisibility(View.GONE);
-		}
-		builder.setView(binding.getRoot());
+    public static void show(final XmppActivity xmppActivity, final Blockable blockable) {
+        show(xmppActivity, blockable, null);
+    }
+
+    public static void show(
+            final XmppActivity xmppActivity, final Blockable blockable, final String serverMsgId) {
+        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(xmppActivity);
+        final boolean isBlocked = blockable.isBlocked();
+        builder.setNegativeButton(R.string.cancel, null);
+        DialogBlockContactBinding binding =
+                DataBindingUtil.inflate(
+                        xmppActivity.getLayoutInflater(),
+                        R.layout.dialog_block_contact,
+                        null,
+                        false);
+        final boolean reporting =
+                blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
+        if (reporting && !isBlocked) {
+            binding.reportSpam.setVisibility(View.VISIBLE);
+            if (serverMsgId != null) {
+                binding.reportSpam.setChecked(true);
+                binding.reportSpam.setEnabled(false);
+            } else {
+                binding.reportSpam.setEnabled(true);
+            }
+        } else {
+            binding.reportSpam.setVisibility(View.GONE);
+        }
+        builder.setView(binding.getRoot());
 
-		final String value;
-		@StringRes int res;
-		if (blockable.getJid().isFullJid()) {
-			builder.setTitle(isBlocked ? R.string.action_unblock_participant : R.string.action_block_participant);
-			value = blockable.getJid().toEscapedString();
-			res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text;
-		} else if (blockable.getJid().getLocal() == null || blockable.getAccount().isBlocked(blockable.getJid().getDomain())) {
-			builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
-			value =blockable.getJid().getDomain().toEscapedString();
-			res = isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text;
-		} else {
-			if (isBlocked) {
-				builder.setTitle(R.string.action_unblock_contact);
-			} else if (serverMsgId != null) {
-				builder.setTitle(R.string.report_spam_and_block);
+        final String value;
+        @StringRes int res;
+        if (blockable.getJid().isFullJid()) {
+            builder.setTitle(
+                    isBlocked
+                            ? R.string.action_unblock_participant
+                            : R.string.action_block_participant);
+            value = blockable.getJid().toString();
+            res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text;
+        } else if (blockable.getJid().getLocal() == null
+                || blockable.getAccount().isBlocked(blockable.getJid().getDomain())) {
+            builder.setTitle(
+                    isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
+            value = blockable.getJid().getDomain().toString();
+            res = isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text;
+        } else {
+            if (isBlocked) {
+                builder.setTitle(R.string.action_unblock_contact);
+            } else if (serverMsgId != null) {
+                builder.setTitle(R.string.report_spam_and_block);
             } else {
                 final int resBlockAction =
                         blockable instanceof Conversation
@@ -61,28 +70,39 @@ public final class BlockContactDialog {
                                 ? R.string.block_stranger
                                 : R.string.action_block_contact;
                 builder.setTitle(resBlockAction);
-			}
-			value = blockable.getJid().asBareJid().toEscapedString();
-			res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text;
-		}
-		binding.text.setText(JidDialog.style(xmppActivity, res, value));
-		builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, (dialog, which) -> {
-			if (isBlocked) {
-				xmppActivity.xmppConnectionService.sendUnblockRequest(blockable);
-			} else {
-				boolean toastShown = false;
-				if (xmppActivity.xmppConnectionService.sendBlockRequest(blockable, binding.reportSpam.isChecked(), serverMsgId)) {
-					Toast.makeText(xmppActivity, R.string.corresponding_chats_closed, Toast.LENGTH_SHORT).show();
-					toastShown = true;
-				}
-				if (xmppActivity instanceof ContactDetailsActivity) {
-					if (!toastShown) {
-						Toast.makeText(xmppActivity, R.string.contact_blocked_past_tense, Toast.LENGTH_SHORT).show();
-					}
-					xmppActivity.finish();
-				}
-			}
-		});
-		builder.create().show();
-	}
+            }
+            value = blockable.getJid().asBareJid().toString();
+            res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text;
+        }
+        binding.text.setText(JidDialog.style(xmppActivity, res, value));
+        builder.setPositiveButton(
+                isBlocked ? R.string.unblock : R.string.block,
+                (dialog, which) -> {
+                    if (isBlocked) {
+                        xmppActivity.xmppConnectionService.sendUnblockRequest(blockable);
+                    } else {
+                        boolean toastShown = false;
+                        if (xmppActivity.xmppConnectionService.sendBlockRequest(
+                                blockable, binding.reportSpam.isChecked(), serverMsgId)) {
+                            Toast.makeText(
+                                            xmppActivity,
+                                            R.string.corresponding_chats_closed,
+                                            Toast.LENGTH_SHORT)
+                                    .show();
+                            toastShown = true;
+                        }
+                        if (xmppActivity instanceof ContactDetailsActivity) {
+                            if (!toastShown) {
+                                Toast.makeText(
+                                                xmppActivity,
+                                                R.string.contact_blocked_past_tense,
+                                                Toast.LENGTH_SHORT)
+                                        .show();
+                            }
+                            xmppActivity.finish();
+                        }
+                    }
+                });
+        builder.create().show();
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
index 0a59f5348..71e1f25aa 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
@@ -3,12 +3,8 @@ package eu.siacs.conversations.ui;
 import android.os.Bundle;
 import android.text.Editable;
 import android.widget.Toast;
-
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentTransaction;
-
-import java.util.Collections;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Blockable;
@@ -17,97 +13,107 @@ import eu.siacs.conversations.entities.RawBlockable;
 import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
+import java.util.Collections;
 
-public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {
-
-	private Account account = null;
+public class BlocklistActivity extends AbstractSearchableListItemActivity
+        implements OnUpdateBlocklist {
 
-	@Override
-	public void onCreate(final Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		getListView().setOnItemLongClickListener((parent, view, position, id) -> {
-			BlockContactDialog.show(BlocklistActivity.this, (Blockable) getListItems().get(position));
-			return true;
-		});
-		this.binding.fab.show();
-		this.binding.fab.setOnClickListener((v)->showEnterJidDialog());
-	}
+    private Account account = null;
 
-	@Override
-	public void onBackendConnected() {
-		for (final Account account : xmppConnectionService.getAccounts()) {
-			if (account.getJid().toEscapedString().equals(getIntent().getStringExtra(EXTRA_ACCOUNT))) {
-				this.account = account;
-				break;
-			}
-		}
-		filterContacts();
-		Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
-		if (fragment instanceof OnBackendConnected) {
-			((OnBackendConnected) fragment).onBackendConnected();
-		}
-	}
+    @Override
+    public void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getListView()
+                .setOnItemLongClickListener(
+                        (parent, view, position, id) -> {
+                            BlockContactDialog.show(
+                                    BlocklistActivity.this,
+                                    (Blockable) getListItems().get(position));
+                            return true;
+                        });
+        this.binding.fab.show();
+        this.binding.fab.setOnClickListener((v) -> showEnterJidDialog());
+    }
 
-	@Override
-	protected void filterContacts(final String needle) {
-		getListItems().clear();
-		if (account != null) {
-			for (final Jid jid : account.getBlocklist()) {
-				ListItem item;
-				if (jid.isFullJid()) {
-					item = new RawBlockable(account, jid);
-				} else {
-					item = account.getRoster().getContact(jid);
-				}
-				if (item.match(this, needle)) {
-					getListItems().add(item);
-				}
-			}
-			Collections.sort(getListItems());
-		}
-		getListItemAdapter().notifyDataSetChanged();
-	}
+    @Override
+    public void onBackendConnected() {
+        for (final Account account : xmppConnectionService.getAccounts()) {
+            if (account.getJid().toString().equals(getIntent().getStringExtra(EXTRA_ACCOUNT))) {
+                this.account = account;
+                break;
+            }
+        }
+        filterContacts();
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
+        if (fragment instanceof OnBackendConnected) {
+            ((OnBackendConnected) fragment).onBackendConnected();
+        }
+    }
 
-	protected void showEnterJidDialog() {
-		FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
-		Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
-		if (prev != null) {
-			ft.remove(prev);
-		}
-		ft.addToBackStack(null);
-		EnterJidDialog dialog = EnterJidDialog.newInstance(
-				null,
-				getString(R.string.block_jabber_id),
-				getString(R.string.block),
-				null,
-				account.getJid().asBareJid().toEscapedString(),
-				true,
-				false
-		);
+    @Override
+    protected void filterContacts(final String needle) {
+        getListItems().clear();
+        if (account != null) {
+            for (final Jid jid : account.getBlocklist()) {
+                ListItem item;
+                if (jid.isFullJid()) {
+                    item = new RawBlockable(account, jid);
+                } else {
+                    item = account.getRoster().getContact(jid);
+                }
+                if (item.match(this, needle)) {
+                    getListItems().add(item);
+                }
+            }
+            Collections.sort(getListItems());
+        }
+        getListItemAdapter().notifyDataSetChanged();
+    }
 
-		dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
-			Blockable blockable = new RawBlockable(account, contactJid);
-			if (xmppConnectionService.sendBlockRequest(blockable, false, null)) {
-				Toast.makeText(BlocklistActivity.this, R.string.corresponding_chats_closed, Toast.LENGTH_SHORT).show();
-			}
-			return true;
-		});
+    protected void showEnterJidDialog() {
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
+        if (prev != null) {
+            ft.remove(prev);
+        }
+        ft.addToBackStack(null);
+        EnterJidDialog dialog =
+                EnterJidDialog.newInstance(
+                        null,
+                        getString(R.string.block_jabber_id),
+                        getString(R.string.block),
+                        null,
+                        account.getJid().asBareJid().toString(),
+                        true,
+                        false);
 
-		dialog.show(ft, "dialog");
-	}
+        dialog.setOnEnterJidDialogPositiveListener(
+                (accountJid, contactJid) -> {
+                    Blockable blockable = new RawBlockable(account, contactJid);
+                    if (xmppConnectionService.sendBlockRequest(blockable, false, null)) {
+                        Toast.makeText(
+                                        BlocklistActivity.this,
+                                        R.string.corresponding_chats_closed,
+                                        Toast.LENGTH_SHORT)
+                                .show();
+                    }
+                    return true;
+                });
 
-	protected void refreshUiReal() {
-		final Editable editable = getSearchEditText().getText();
-		if (editable != null) {
-			filterContacts(editable.toString());
-		} else {
-			filterContacts();
-		}
-	}
+        dialog.show(ft, "dialog");
+    }
 
-	@Override
-	public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
-		refreshUi();
-	}
+    protected void refreshUiReal() {
+        final Editable editable = getSearchEditText().getText();
+        if (editable != null) {
+            filterContacts(editable.toString());
+        } else {
+            filterContacts();
+        }
+    }
 
+    @Override
+    public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
+        refreshUi();
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java
index 420494d4d..a13324730 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java
@@ -294,7 +294,7 @@ public class ChannelDiscoveryActivity extends XmppActivity
     }
 
     public void joinChannelSearchResult(final String selectedAccount, final Room result) {
-        final Jid jid = Jid.ofEscaped(selectedAccount);
+        final Jid jid = Jid.of(selectedAccount);
         final Account account = xmppConnectionService.findAccountByJid(jid);
         final Conversation conversation =
                 xmppConnectionService.findOrCreateConversation(
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java
index 71662589c..ff60e6419 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseAccountForProfilePictureActivity.java
@@ -4,14 +4,11 @@ import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.widget.Toast;
-
 import androidx.databinding.DataBindingUtil;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityManageAccountsBinding;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.ui.adapter.AccountAdapter;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,16 +26,18 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        final ActivityManageAccountsBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_manage_accounts);
+        final ActivityManageAccountsBinding binding =
+                DataBindingUtil.setContentView(this, R.layout.activity_manage_accounts);
         Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
         setSupportActionBar(binding.toolbar);
         configureActionBar(getSupportActionBar(), false);
         this.mAccountAdapter = new AccountAdapter(this, accountList, false);
         binding.accountList.setAdapter(this.mAccountAdapter);
-        binding.accountList.setOnItemClickListener((arg0, view, position, arg3) -> {
-            final Account account = accountList.get(position);
-            goToProfilePictureActivity(account);
-        });
+        binding.accountList.setOnItemClickListener(
+                (arg0, view, position, arg3) -> {
+                    final Account account = accountList.get(position);
+                    goToProfilePictureActivity(account);
+                });
     }
 
     @Override
@@ -58,7 +57,7 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
 
     private void loadEnabledAccounts() {
         accountList.clear();
-        for(Account account : xmppConnectionService.getAccounts()) {
+        for (Account account : xmppConnectionService.getAccounts()) {
             if (account.isEnabled()) {
                 accountList.add(account);
             }
@@ -70,13 +69,17 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
         final Uri uri = startIntent == null ? null : startIntent.getData();
         if (uri != null) {
             Intent intent = new Intent(this, PublishProfilePictureActivity.class);
-            intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
+            intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
             intent.setData(uri);
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
             try {
                 startActivity(intent);
             } catch (SecurityException e) {
-                Toast.makeText(this, R.string.sharing_application_not_grant_permission, Toast.LENGTH_SHORT).show();
+                Toast.makeText(
+                                this,
+                                R.string.sharing_application_not_grant_permission,
+                                Toast.LENGTH_SHORT)
+                        .show();
                 return;
             }
         }
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
index b9ebb413d..b6bcafbbc 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -16,23 +16,12 @@ import android.widget.AbsListView.MultiChoiceModeListener;
 import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.TextView;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
 import androidx.appcompat.app.ActionBar;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentTransaction;
-
 import com.google.common.base.Strings;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
@@ -44,8 +33,15 @@ import eu.siacs.conversations.ui.util.ActivityResult;
 import eu.siacs.conversations.ui.util.PendingItem;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
-public class ChooseContactActivity extends AbstractSearchableListItemActivity implements MultiChoiceModeListener, AdapterView.OnItemClickListener {
+public class ChooseContactActivity extends AbstractSearchableListItemActivity
+        implements MultiChoiceModeListener, AdapterView.OnItemClickListener {
     public static final String EXTRA_TITLE_RES_ID = "extra_title_res_id";
     public static final String EXTRA_GROUP_CHAT_NAME = "extra_group_chat_name";
     public static final String EXTRA_SELECT_MULTIPLE = "extra_select_multiple";
@@ -75,11 +71,11 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         } else {
             contacts.add(conversation.getJid().asBareJid().toString());
         }
-        intent.putExtra(EXTRA_FILTERED_CONTACTS, contacts.toArray(new String[contacts.size()]));
+        intent.putExtra(EXTRA_FILTERED_CONTACTS, contacts.toArray(new String[0]));
         intent.putExtra(EXTRA_CONVERSATION, conversation.getUuid());
         intent.putExtra(EXTRA_SELECT_MULTIPLE, true);
         intent.putExtra(EXTRA_SHOW_ENTER_JID, true);
-        intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toEscapedString());
+        intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
         return intent;
     }
 
@@ -135,8 +131,11 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         }
 
         final SharedPreferences preferences = getPreferences();
-        this.startSearching = intent.getBooleanExtra("direct_search", false) && preferences.getBoolean("start_searching", getResources().getBoolean(R.bool.start_searching));
-
+        this.startSearching =
+                intent.getBooleanExtra("direct_search", false)
+                        && preferences.getBoolean(
+                                "start_searching",
+                                getResources().getBoolean(R.bool.start_searching));
     }
 
     private void onFabClicked(View v) {
@@ -159,9 +158,11 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         binding.fab.setImageResource(R.drawable.ic_navigate_next_24dp);
         binding.fab.show();
         final View view = getSearchEditText();
-        final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+        final InputMethodManager imm =
+                (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
         if (view != null && imm != null) {
-            imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
+            imm.hideSoftInputFromWindow(
+                    getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
         }
         return true;
     }
@@ -226,11 +227,14 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         }
     }
 
-    public @StringRes
-    int getTitleFromIntent() {
+    public @StringRes int getTitleFromIntent() {
         final Intent intent = getIntent();
         boolean multiple = intent != null && intent.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false);
-        @StringRes int fallback = multiple ? R.string.title_activity_choose_contacts : R.string.title_activity_choose_contact;
+        @StringRes
+        int fallback =
+                multiple
+                        ? R.string.title_activity_choose_contacts
+                        : R.string.title_activity_choose_contact;
         return intent != null ? intent.getIntExtra(EXTRA_TITLE_RES_ID, fallback) : fallback;
     }
 
@@ -239,7 +243,8 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         super.onCreateOptionsMenu(menu);
         final Intent i = getIntent();
         boolean showEnterJid = i != null && i.getBooleanExtra(EXTRA_SHOW_ENTER_JID, false);
-        menu.findItem(R.id.action_scan_qr_code).setVisible(isCameraFeatureAvailable() && showEnterJid);
+        menu.findItem(R.id.action_scan_qr_code)
+                .setVisible(isCameraFeatureAvailable() && showEnterJid);
         MenuItem mMenuSearchView = menu.findItem(R.id.action_search);
         if (startSearching) {
             mMenuSearchView.expandActionView();
@@ -276,8 +281,8 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         for (final Account account : xmppConnectionService.getAccounts()) {
             if (account.isEnabled()) {
                 for (final Contact contact : account.getRoster().getContacts()) {
-                    if (contact.showInContactList() &&
-                            !filterContacts.contains(contact.getJid().asBareJid().toString())
+                    if (contact.showInContactList()
+                            && !filterContacts.contains(contact.getJid().asBareJid().toString())
                             && contact.match(this, needle)) {
                         getListItems().add(contact);
                     }
@@ -293,7 +298,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
     }
 
     public void refreshUiReal() {
-        //nothing to do. This Activity doesn't implement any listeners
+        // nothing to do. This Activity doesn't implement any listeners
     }
 
     @Override
@@ -314,28 +319,29 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         }
         ft.addToBackStack(null);
         Jid jid = uri == null ? null : uri.getJid();
-        EnterJidDialog dialog = EnterJidDialog.newInstance(
-                mActivatedAccounts,
-                getString(R.string.enter_contact),
-                getString(R.string.select),
-                jid == null ? null : jid.asBareJid().toString(),
-                getIntent().getStringExtra(EXTRA_ACCOUNT),
-                true,
-                false
-        );
-
-        dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
-            final Intent request = getIntent();
-            final Intent data = new Intent();
-            data.putExtra("contact", contactJid.toString());
-            data.putExtra(EXTRA_ACCOUNT, accountJid.toEscapedString());
-            data.putExtra(EXTRA_SELECT_MULTIPLE, false);
-            copy(request, data);
-            setResult(RESULT_OK, data);
-            finish();
-
-            return true;
-        });
+        EnterJidDialog dialog =
+                EnterJidDialog.newInstance(
+                        mActivatedAccounts,
+                        getString(R.string.enter_contact),
+                        getString(R.string.select),
+                        jid == null ? null : jid.asBareJid().toString(),
+                        getIntent().getStringExtra(EXTRA_ACCOUNT),
+                        true,
+                        false);
+
+        dialog.setOnEnterJidDialogPositiveListener(
+                (accountJid, contactJid) -> {
+                    final Intent request = getIntent();
+                    final Intent data = new Intent();
+                    data.putExtra("contact", contactJid.toString());
+                    data.putExtra(EXTRA_ACCOUNT, accountJid.toString());
+                    data.putExtra(EXTRA_SELECT_MULTIPLE, false);
+                    copy(request, data);
+                    setResult(RESULT_OK, data);
+                    finish();
+
+                    return true;
+                });
 
         dialog.show(ft, "dialog");
     }
@@ -352,7 +358,8 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
     }
 
     private void handleActivityResult(ActivityResult activityResult) {
-        if (activityResult.resultCode == RESULT_OK && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
+        if (activityResult.resultCode == RESULT_OK
+                && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
             String result = activityResult.data.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
             XmppUri uri = new XmppUri(Strings.nullToEmpty(result));
             if (uri.isValidJid()) {
@@ -367,21 +374,23 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         this.mActivatedAccounts.clear();
         for (final Account account : xmppConnectionService.getAccounts()) {
             if (account.isEnabled()) {
-                this.mActivatedAccounts.add(account.getJid().asBareJid().toEscapedString());
+                this.mActivatedAccounts.add(account.getJid().asBareJid().toString());
             }
         }
         ActivityResult activityResult = this.postponedActivityResult.pop();
         if (activityResult != null) {
             handleActivityResult(activityResult);
         }
-        final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
+        final Fragment fragment =
+                getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_DIALOG);
         if (fragment instanceof OnBackendConnected) {
             ((OnBackendConnected) fragment).onBackendConnected();
         }
     }
 
     @Override
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         ScanActivity.onRequestPermissionResult(this, requestCode, grantResults);
     }
@@ -393,8 +402,10 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
             getListView().setItemChecked(position, true);
             return;
         }
-        final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-        imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
+        final InputMethodManager imm =
+                (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.hideSoftInputFromWindow(
+                getSearchEditText().getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
         final ListItem mListItem = getListItems().get(position);
         onListItemClicked(mListItem);
     }
@@ -405,7 +416,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
         data.putExtra("contact", item.getJid().toString());
         String account = request.getStringExtra(EXTRA_ACCOUNT);
         if (account == null && item instanceof Contact) {
-            account = ((Contact) item).getAccount().getJid().asBareJid().toEscapedString();
+            account = ((Contact) item).getAccount().getJid().asBareJid().toString();
         }
         data.putExtra(EXTRA_ACCOUNT, account);
         data.putExtra(EXTRA_SELECT_MULTIPLE, false);
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index 8a1a0a133..bc526d8c2 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -405,8 +405,7 @@ public class ConferenceDetailsActivity extends XmppActivity
         if (mConversation != null) {
             if (http) {
                 return "https://conversations.im/j/"
-                        + XmppUri.lameUrlEncode(
-                                mConversation.getJid().asBareJid().toEscapedString());
+                        + XmppUri.lameUrlEncode(mConversation.getJid().asBareJid().toString());
             } else {
                 return "xmpp:" + mConversation.getJid().asBareJid() + "?join";
             }
@@ -522,7 +521,7 @@ public class ConferenceDetailsActivity extends XmppActivity
         }
         final MucOptions mucOptions = mConversation.getMucOptions();
         final User self = mucOptions.getSelf();
-        final String account = mConversation.getAccount().getJid().asBareJid().toEscapedString();
+        final String account = mConversation.getAccount().getJid().asBareJid().toString();
         setTitle(
                 mucOptions.isPrivateAndNonAnonymous()
                         ? R.string.action_muc_details
@@ -537,7 +536,7 @@ public class ConferenceDetailsActivity extends XmppActivity
             this.binding.jid.setText(
                     getString(R.string.hosted_on, mConversation.getJid().getDomain()));
         } else {
-            this.binding.jid.setText(mConversation.getJid().asBareJid().toEscapedString());
+            this.binding.jid.setText(mConversation.getJid().asBareJid().toString());
         }
         AvatarWorkerTask.loadAvatar(
                 mConversation, binding.yourPhoto, R.dimen.avatar_on_details_screen_size);
@@ -682,7 +681,7 @@ public class ConferenceDetailsActivity extends XmppActivity
 
     @Override
     public void onAffiliationChangeFailed(Jid jid, int resId) {
-        displayToast(getString(resId, jid.asBareJid().toEscapedString()));
+        displayToast(getString(resId, jid.asBareJid().toString()));
     }
 
     @Override
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index 69e78124f..3738f8641 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -172,7 +172,7 @@ public class ContactDetailsActivity extends OmemoActivity
         if (quicksyContact) {
             value = PhoneNumberUtilWrapper.toFormattedPhoneNumber(this, jid);
         } else {
-            value = jid.toEscapedString();
+            value = jid.toString();
         }
         final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
         builder.setTitle(getString(R.string.action_add_phone_book));
@@ -233,9 +233,9 @@ public class ContactDetailsActivity extends OmemoActivity
     protected String getShareableUri(boolean http) {
         if (http) {
             return "https://conversations.im/i/"
-                    + XmppUri.lameUrlEncode(contact.getJid().asBareJid().toEscapedString());
+                    + XmppUri.lameUrlEncode(contact.getJid().asBareJid().toString());
         } else {
-            return "xmpp:" + contact.getJid().asBareJid().toEscapedString();
+            return "xmpp:" + contact.getJid().asBareJid().toString();
         }
     }
 
@@ -247,11 +247,11 @@ public class ContactDetailsActivity extends OmemoActivity
                         && savedInstanceState.getBoolean("show_inactive_omemo", false);
         if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
             try {
-                this.accountJid = Jid.ofEscaped(getIntent().getExtras().getString(EXTRA_ACCOUNT));
+                this.accountJid = Jid.of(getIntent().getExtras().getString(EXTRA_ACCOUNT));
             } catch (final IllegalArgumentException ignored) {
             }
             try {
-                this.contactJid = Jid.ofEscaped(getIntent().getExtras().getString("contact"));
+                this.contactJid = Jid.of(getIntent().getExtras().getString("contact"));
             } catch (final IllegalArgumentException ignored) {
             }
         }
@@ -328,7 +328,7 @@ public class ContactDetailsActivity extends OmemoActivity
                                 JidDialog.style(
                                         this,
                                         R.string.remove_contact_text,
-                                        contact.getJid().toEscapedString()))
+                                        contact.getJid().toString()))
                         .setPositiveButton(getString(R.string.delete), removeFromRoster)
                         .create()
                         .show();
@@ -506,7 +506,7 @@ public class ContactDetailsActivity extends OmemoActivity
         }
 
         binding.detailsContactjid.setText(IrregularUnicodeDetector.style(this, contact.getJid()));
-        final String account = contact.getAccount().getJid().asBareJid().toEscapedString();
+        final String account = contact.getAccount().getJid().asBareJid().toString();
         binding.detailsAccount.setText(getString(R.string.using_account, account));
         AvatarWorkerTask.loadAvatar(
                 contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size);
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index c3b0bcd9d..20ed93c05 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -902,8 +902,7 @@ public class ConversationFragment extends XmppFragment
             }
             intent.putExtra("contacts", contacts);
             intent.putExtra(
-                    EXTRA_ACCOUNT,
-                    conversation.getAccount().getJid().asBareJid().toEscapedString());
+                    EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
             intent.putExtra("conversation", conversation.getUuid());
             startActivityForResult(intent, requestCode);
             return true;
@@ -1562,8 +1561,8 @@ public class ConversationFragment extends XmppFragment
             intent.setAction(Intent.ACTION_VIEW);
             intent.putExtra(
                     RtpSessionActivity.EXTRA_ACCOUNT,
-                    id.getAccount().getJid().asBareJid().toEscapedString());
-            intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.getWith().toEscapedString());
+                    id.getAccount().getJid().asBareJid().toString());
+            intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.getWith().toString());
             if (id instanceof AbstractJingleConnection) {
                 intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.getSessionId());
                 startActivity(intent);
@@ -3570,7 +3569,7 @@ public class ConversationFragment extends XmppFragment
                                                     + message.getContact()
                                                             .getJid()
                                                             .asBareJid()
-                                                            .toEscapedString());
+                                                            .toString());
                                     break;
                             }
                             return true;
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
index 59776a0ab..04853ddd2 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
@@ -29,12 +29,13 @@
 
 package eu.siacs.conversations.ui;
 
+import static androidx.recyclerview.widget.ItemTouchHelper.LEFT;
+import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
+
 import android.app.Activity;
-import android.app.AlertDialog;
 import android.app.Fragment;
 import android.content.Intent;
 import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -44,22 +45,14 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.databinding.DataBindingUtil;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
-
-import com.google.android.material.color.MaterialColors;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.snackbar.Snackbar;
 import com.google.common.collect.Collections2;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-
 import eu.siacs.conversations.BuildConfig;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
@@ -77,227 +70,281 @@ import eu.siacs.conversations.ui.util.PendingItem;
 import eu.siacs.conversations.ui.util.ScrollState;
 import eu.siacs.conversations.utils.AccountUtils;
 import eu.siacs.conversations.utils.EasyOnboardingInvite;
-
-import static androidx.recyclerview.widget.ItemTouchHelper.LEFT;
-import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 public class ConversationsOverviewFragment extends XmppFragment {
 
-	private static final String STATE_SCROLL_POSITION = ConversationsOverviewFragment.class.getName()+".scroll_state";
-
-	private final List<Conversation> conversations = new ArrayList<>();
-	private final PendingItem<Conversation> swipedConversation = new PendingItem<>();
-	private final PendingItem<ScrollState> pendingScrollState = new PendingItem<>();
-	private FragmentConversationsOverviewBinding binding;
-	private ConversationAdapter conversationsAdapter;
-	private XmppActivity activity;
-	private final PendingActionHelper pendingActionHelper = new PendingActionHelper();
-
-	private final ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0,LEFT|RIGHT) {
-		@Override
-		public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
-			return false;
-		}
-
-		@Override
-		public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
-								float dX, float dY, int actionState, boolean isCurrentlyActive) {
-			if (viewHolder instanceof ConversationAdapter.ConversationViewHolder conversationViewHolder) {
-				getDefaultUIUtil().onDraw(c,recyclerView,conversationViewHolder.binding.frame,dX,dY,actionState,isCurrentlyActive);
-			}
-		}
-
-		@Override
-		public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
-			if (viewHolder instanceof ConversationAdapter.ConversationViewHolder conversationViewHolder) {
-				getDefaultUIUtil().clearView(conversationViewHolder.binding.frame);
-			}
-		}
-
-		@Override
-		public float getSwipeEscapeVelocity(final float defaultEscapeVelocity) {
-			return 32 * defaultEscapeVelocity;
-		}
-
-		@Override
-		public void onSwiped(final RecyclerView.ViewHolder viewHolder, final int direction) {
-			pendingActionHelper.execute();
-			int position = viewHolder.getLayoutPosition();
-			try {
-				swipedConversation.push(conversations.get(position));
-			} catch (IndexOutOfBoundsException e) {
-				return;
-			}
-			conversationsAdapter.remove(swipedConversation.peek(), position);
-			activity.xmppConnectionService.markRead(swipedConversation.peek());
-
-			if (position == 0 && conversationsAdapter.getItemCount() == 0) {
-				final Conversation c = swipedConversation.pop();
-				activity.xmppConnectionService.archiveConversation(c);
-				return;
-			}
-			final boolean formerlySelected = ConversationFragment.getConversation(getActivity()) == swipedConversation.peek();
-			if (activity instanceof OnConversationArchived) {
-				((OnConversationArchived) activity).onConversationArchived(swipedConversation.peek());
-			}
-			final Conversation c = swipedConversation.peek();
-			final int title;
-			if (c.getMode() == Conversational.MODE_MULTI) {
-				if (c.getMucOptions().isPrivateAndNonAnonymous()) {
-					title = R.string.title_undo_swipe_out_group_chat;
-				} else {
-					title = R.string.title_undo_swipe_out_channel;
-				}
-			} else {
-				title = R.string.title_undo_swipe_out_chat;
-			}
-
-			final Snackbar snackbar = Snackbar.make(binding.list, title, 5000)
-					.setAction(R.string.undo, v -> {
-						pendingActionHelper.undo();
-						Conversation conversation = swipedConversation.pop();
-						conversationsAdapter.insert(conversation, position);
-						if (formerlySelected) {
-							if (activity instanceof OnConversationSelected) {
-								((OnConversationSelected) activity).onConversationSelected(c);
-							}
-						}
-						LinearLayoutManager layoutManager = (LinearLayoutManager) binding.list.getLayoutManager();
-						if (position > layoutManager.findLastVisibleItemPosition()) {
-							binding.list.smoothScrollToPosition(position);
-						}
-					})
-					.addCallback(new Snackbar.Callback() {
-						@Override
-						public void onDismissed(Snackbar transientBottomBar, int event) {
-							switch (event) {
-								case DISMISS_EVENT_SWIPE:
-								case DISMISS_EVENT_TIMEOUT:
-									pendingActionHelper.execute();
-									break;
-							}
-						}
-					});
-
-			pendingActionHelper.push(() -> {
-				if (snackbar.isShownOrQueued()) {
-					snackbar.dismiss();
-				}
-				final Conversation conversation = swipedConversation.pop();
-				if(conversation != null){
-					if (!conversation.isRead() && conversation.getMode() == Conversation.MODE_SINGLE) {
-						return;
-					}
-					activity.xmppConnectionService.archiveConversation(c);
-				}
-			});
-			snackbar.show();
-		}
-	};
-
-	private ItemTouchHelper touchHelper;
-
-	public static Conversation getSuggestion(Activity activity) {
-		final Conversation exception;
-		Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
-		if (fragment instanceof ConversationsOverviewFragment) {
-			exception = ((ConversationsOverviewFragment) fragment).swipedConversation.peek();
-		} else {
-			exception = null;
-		}
-		return getSuggestion(activity, exception);
-	}
-
-	public static Conversation getSuggestion(Activity activity, Conversation exception) {
-		Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
-		if (fragment instanceof ConversationsOverviewFragment) {
-			List<Conversation> conversations = ((ConversationsOverviewFragment) fragment).conversations;
-			if (conversations.size() > 0) {
-				Conversation suggestion = conversations.get(0);
-				if (suggestion == exception) {
-					if (conversations.size() > 1) {
-						return conversations.get(1);
-					}
-				} else {
-					return suggestion;
-				}
-			}
-		}
-		return null;
-
-	}
-
-	@Override
-	public void onActivityCreated(Bundle savedInstanceState) {
-		super.onActivityCreated(savedInstanceState);
-		if (savedInstanceState == null) {
-			return;
-		}
-		pendingScrollState.push(savedInstanceState.getParcelable(STATE_SCROLL_POSITION));
-	}
-
-	@Override
-	public void onAttach(Activity activity) {
-		super.onAttach(activity);
-		if (activity instanceof XmppActivity) {
-			this.activity = (XmppActivity) activity;
-		} else {
-			throw new IllegalStateException("Trying to attach fragment to activity that is not an XmppActivity");
-		}
-	}
-	@Override
-	public void onDestroyView() {
-		Log.d(Config.LOGTAG,"ConversationsOverviewFragment.onDestroyView()");
-		super.onDestroyView();
-		this.binding = null;
-		this.conversationsAdapter = null;
-		this.touchHelper = null;
-	}
-	@Override
-	public void onDestroy() {
-		Log.d(Config.LOGTAG,"ConversationsOverviewFragment.onDestroy()");
-		super.onDestroy();
-
-	}
-	@Override
-	public void onPause() {
-		Log.d(Config.LOGTAG,"ConversationsOverviewFragment.onPause()");
-		pendingActionHelper.execute();
-		super.onPause();
-	}
-
-	@Override
-	public void onDetach() {
-		super.onDetach();
-		this.activity = null;
-	}
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		setHasOptionsMenu(true);
-	}
-
-	@Override
-	public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-		this.binding = DataBindingUtil.inflate(inflater, R.layout.fragment_conversations_overview, container, false);
-		this.binding.fab.setOnClickListener((view) -> StartConversationActivity.launch(getActivity()));
-
-		this.conversationsAdapter = new ConversationAdapter(this.activity, this.conversations);
-		this.conversationsAdapter.setConversationClickListener((view, conversation) -> {
-			if (activity instanceof OnConversationSelected) {
-				((OnConversationSelected) activity).onConversationSelected(conversation);
-			} else {
-				Log.w(ConversationsOverviewFragment.class.getCanonicalName(), "Activity does not implement OnConversationSelected");
-			}
-		});
-		this.binding.list.setAdapter(this.conversationsAdapter);
-		this.binding.list.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false));
-		this.binding.list.addOnScrollListener(ExtendedFabSizeChanger.of(binding.fab));
-		this.touchHelper = new ItemTouchHelper(this.callback);
-		this.touchHelper.attachToRecyclerView(this.binding.list);
-		return binding.getRoot();
-	}
+    private static final String STATE_SCROLL_POSITION =
+            ConversationsOverviewFragment.class.getName() + ".scroll_state";
+
+    private final List<Conversation> conversations = new ArrayList<>();
+    private final PendingItem<Conversation> swipedConversation = new PendingItem<>();
+    private final PendingItem<ScrollState> pendingScrollState = new PendingItem<>();
+    private FragmentConversationsOverviewBinding binding;
+    private ConversationAdapter conversationsAdapter;
+    private XmppActivity activity;
+    private final PendingActionHelper pendingActionHelper = new PendingActionHelper();
+
+    private final ItemTouchHelper.SimpleCallback callback =
+            new ItemTouchHelper.SimpleCallback(0, LEFT | RIGHT) {
+                @Override
+                public boolean onMove(
+                        @NonNull RecyclerView recyclerView,
+                        @NonNull RecyclerView.ViewHolder viewHolder,
+                        @NonNull RecyclerView.ViewHolder target) {
+                    return false;
+                }
+
+                @Override
+                public void onChildDraw(
+                        @NonNull Canvas c,
+                        @NonNull RecyclerView recyclerView,
+                        @NonNull RecyclerView.ViewHolder viewHolder,
+                        float dX,
+                        float dY,
+                        int actionState,
+                        boolean isCurrentlyActive) {
+                    if (viewHolder
+                            instanceof
+                            ConversationAdapter.ConversationViewHolder conversationViewHolder) {
+                        getDefaultUIUtil()
+                                .onDraw(
+                                        c,
+                                        recyclerView,
+                                        conversationViewHolder.binding.frame,
+                                        dX,
+                                        dY,
+                                        actionState,
+                                        isCurrentlyActive);
+                    }
+                }
+
+                @Override
+                public void clearView(
+                        @NonNull RecyclerView recyclerView,
+                        @NonNull RecyclerView.ViewHolder viewHolder) {
+                    if (viewHolder
+                            instanceof
+                            ConversationAdapter.ConversationViewHolder conversationViewHolder) {
+                        getDefaultUIUtil().clearView(conversationViewHolder.binding.frame);
+                    }
+                }
+
+                @Override
+                public float getSwipeEscapeVelocity(final float defaultEscapeVelocity) {
+                    return 32 * defaultEscapeVelocity;
+                }
+
+                @Override
+                public void onSwiped(
+                        final RecyclerView.ViewHolder viewHolder, final int direction) {
+                    pendingActionHelper.execute();
+                    int position = viewHolder.getLayoutPosition();
+                    try {
+                        swipedConversation.push(conversations.get(position));
+                    } catch (IndexOutOfBoundsException e) {
+                        return;
+                    }
+                    conversationsAdapter.remove(swipedConversation.peek(), position);
+                    activity.xmppConnectionService.markRead(swipedConversation.peek());
+
+                    if (position == 0 && conversationsAdapter.getItemCount() == 0) {
+                        final Conversation c = swipedConversation.pop();
+                        activity.xmppConnectionService.archiveConversation(c);
+                        return;
+                    }
+                    final boolean formerlySelected =
+                            ConversationFragment.getConversation(getActivity())
+                                    == swipedConversation.peek();
+                    if (activity instanceof OnConversationArchived) {
+                        ((OnConversationArchived) activity)
+                                .onConversationArchived(swipedConversation.peek());
+                    }
+                    final Conversation c = swipedConversation.peek();
+                    final int title;
+                    if (c.getMode() == Conversational.MODE_MULTI) {
+                        if (c.getMucOptions().isPrivateAndNonAnonymous()) {
+                            title = R.string.title_undo_swipe_out_group_chat;
+                        } else {
+                            title = R.string.title_undo_swipe_out_channel;
+                        }
+                    } else {
+                        title = R.string.title_undo_swipe_out_chat;
+                    }
+
+                    final Snackbar snackbar =
+                            Snackbar.make(binding.list, title, 5000)
+                                    .setAction(
+                                            R.string.undo,
+                                            v -> {
+                                                pendingActionHelper.undo();
+                                                Conversation conversation =
+                                                        swipedConversation.pop();
+                                                conversationsAdapter.insert(conversation, position);
+                                                if (formerlySelected) {
+                                                    if (activity
+                                                            instanceof OnConversationSelected) {
+                                                        ((OnConversationSelected) activity)
+                                                                .onConversationSelected(c);
+                                                    }
+                                                }
+                                                LinearLayoutManager layoutManager =
+                                                        (LinearLayoutManager)
+                                                                binding.list.getLayoutManager();
+                                                if (position
+                                                        > layoutManager
+                                                                .findLastVisibleItemPosition()) {
+                                                    binding.list.smoothScrollToPosition(position);
+                                                }
+                                            })
+                                    .addCallback(
+                                            new Snackbar.Callback() {
+                                                @Override
+                                                public void onDismissed(
+                                                        Snackbar transientBottomBar, int event) {
+                                                    switch (event) {
+                                                        case DISMISS_EVENT_SWIPE:
+                                                        case DISMISS_EVENT_TIMEOUT:
+                                                            pendingActionHelper.execute();
+                                                            break;
+                                                    }
+                                                }
+                                            });
+
+                    pendingActionHelper.push(
+                            () -> {
+                                if (snackbar.isShownOrQueued()) {
+                                    snackbar.dismiss();
+                                }
+                                final Conversation conversation = swipedConversation.pop();
+                                if (conversation != null) {
+                                    if (!conversation.isRead()
+                                            && conversation.getMode() == Conversation.MODE_SINGLE) {
+                                        return;
+                                    }
+                                    activity.xmppConnectionService.archiveConversation(c);
+                                }
+                            });
+                    snackbar.show();
+                }
+            };
+
+    private ItemTouchHelper touchHelper;
+
+    public static Conversation getSuggestion(Activity activity) {
+        final Conversation exception;
+        Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
+        if (fragment instanceof ConversationsOverviewFragment) {
+            exception = ((ConversationsOverviewFragment) fragment).swipedConversation.peek();
+        } else {
+            exception = null;
+        }
+        return getSuggestion(activity, exception);
+    }
+
+    public static Conversation getSuggestion(Activity activity, Conversation exception) {
+        Fragment fragment = activity.getFragmentManager().findFragmentById(R.id.main_fragment);
+        if (fragment instanceof ConversationsOverviewFragment) {
+            List<Conversation> conversations =
+                    ((ConversationsOverviewFragment) fragment).conversations;
+            if (conversations.size() > 0) {
+                Conversation suggestion = conversations.get(0);
+                if (suggestion == exception) {
+                    if (conversations.size() > 1) {
+                        return conversations.get(1);
+                    }
+                } else {
+                    return suggestion;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        if (savedInstanceState == null) {
+            return;
+        }
+        pendingScrollState.push(savedInstanceState.getParcelable(STATE_SCROLL_POSITION));
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        if (activity instanceof XmppActivity) {
+            this.activity = (XmppActivity) activity;
+        } else {
+            throw new IllegalStateException(
+                    "Trying to attach fragment to activity that is not an XmppActivity");
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onDestroyView()");
+        super.onDestroyView();
+        this.binding = null;
+        this.conversationsAdapter = null;
+        this.touchHelper = null;
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onDestroy()");
+        super.onDestroy();
+    }
+
+    @Override
+    public void onPause() {
+        Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onPause()");
+        pendingActionHelper.execute();
+        super.onPause();
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        this.activity = null;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public View onCreateView(
+            final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        this.binding =
+                DataBindingUtil.inflate(
+                        inflater, R.layout.fragment_conversations_overview, container, false);
+        this.binding.fab.setOnClickListener(
+                (view) -> StartConversationActivity.launch(getActivity()));
+
+        this.conversationsAdapter = new ConversationAdapter(this.activity, this.conversations);
+        this.conversationsAdapter.setConversationClickListener(
+                (view, conversation) -> {
+                    if (activity instanceof OnConversationSelected) {
+                        ((OnConversationSelected) activity).onConversationSelected(conversation);
+                    } else {
+                        Log.w(
+                                ConversationsOverviewFragment.class.getCanonicalName(),
+                                "Activity does not implement OnConversationSelected");
+                    }
+                });
+        this.binding.list.setAdapter(this.conversationsAdapter);
+        this.binding.list.setLayoutManager(
+                new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
+        this.binding.list.addOnScrollListener(ExtendedFabSizeChanger.of(binding.fab));
+        this.touchHelper = new ItemTouchHelper(this.callback);
+        this.touchHelper.attachToRecyclerView(this.binding.list);
+        return binding.getRoot();
+    }
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
@@ -313,114 +360,131 @@ public class ConversationsOverviewFragment extends XmppFragment {
                         && QuickConversationsService.isPlayStoreFlavor());
     }
 
-	@Override
-	public void onBackendConnected() {
-		refresh();
-	}
-
-	@Override
-	public void onSaveInstanceState(Bundle bundle) {
-		super.onSaveInstanceState(bundle);
-		ScrollState scrollState = getScrollState();
-		if (scrollState != null) {
-			bundle.putParcelable(STATE_SCROLL_POSITION, scrollState);
-		}
-	}
-
-	private ScrollState getScrollState() {
-		if (this.binding == null) {
-			return null;
-		}
-		LinearLayoutManager layoutManager = (LinearLayoutManager) this.binding.list.getLayoutManager();
-		int position = layoutManager.findFirstVisibleItemPosition();
-		final View view = this.binding.list.getChildAt(0);
-		if (view != null) {
-			return new ScrollState(position,view.getTop());
-		} else {
-			return new ScrollState(position, 0);
-		}
-	}
-
-	@Override
-	public void onStart() {
-		super.onStart();
-		Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onStart()");
-		if (activity.xmppConnectionService != null) {
-			refresh();
-		}
-	}
-
-	@Override
-	public void onResume() {
-		super.onResume();
-		Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onResume()");
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(final MenuItem item) {
-		if (MenuDoubleTabUtil.shouldIgnoreTap()) {
-			return false;
-		}
-		switch (item.getItemId()) {
-			case R.id.action_search:
-				startActivity(new Intent(getActivity(), SearchActivity.class));
-				return true;
-			case R.id.action_easy_invite:
-				selectAccountToStartEasyInvite();
-				return true;
-		}
-		return super.onOptionsItemSelected(item);
-	}
-
-	private void selectAccountToStartEasyInvite() {
-		final List<Account> accounts = EasyOnboardingInvite.getSupportingAccounts(activity.xmppConnectionService);
-		if (accounts.isEmpty()) {
-			//This can technically happen if opening the menu item races with accounts reconnecting or something
-			Toast.makeText(getActivity(),R.string.no_active_accounts_support_this, Toast.LENGTH_LONG).show();
-		} else if (accounts.size() == 1) {
-			openEasyInviteScreen(accounts.get(0));
-		} else {
-			final AtomicReference<Account> selectedAccount = new AtomicReference<>(accounts.get(0));
-			final MaterialAlertDialogBuilder alertDialogBuilder = new MaterialAlertDialogBuilder(activity);
-			alertDialogBuilder.setTitle(R.string.choose_account);
-			final String[] asStrings = Collections2.transform(accounts, a -> a.getJid().asBareJid().toEscapedString()).toArray(new String[0]);
-			alertDialogBuilder.setSingleChoiceItems(asStrings, 0, (dialog, which) -> selectedAccount.set(accounts.get(which)));
-			alertDialogBuilder.setNegativeButton(R.string.cancel, null);
-			alertDialogBuilder.setPositiveButton(R.string.ok, (dialog, which) -> openEasyInviteScreen(selectedAccount.get()));
-			alertDialogBuilder.create().show();
-		}
-	}
-
-	private void openEasyInviteScreen(final Account account) {
-		EasyOnboardingInviteActivity.launch(account, activity);
-	}
-
-	@Override
-	void refresh() {
-		if (this.binding == null || this.activity == null) {
-			Log.d(Config.LOGTAG,"ConversationsOverviewFragment.refresh() skipped updated because view binding or activity was null");
-			return;
-		}
-		this.activity.xmppConnectionService.populateWithOrderedConversations(this.conversations);
-		Conversation removed = this.swipedConversation.peek();
-		if (removed != null) {
-			if (removed.isRead()) {
-				this.conversations.remove(removed);
-			} else {
-				pendingActionHelper.execute();
-			}
-		}
-		this.conversationsAdapter.notifyDataSetChanged();
-		ScrollState scrollState = pendingScrollState.pop();
-		if (scrollState != null) {
-			setScrollPosition(scrollState);
-		}
-	}
-
-	private void setScrollPosition(ScrollState scrollPosition) {
-		if (scrollPosition != null) {
-			LinearLayoutManager layoutManager = (LinearLayoutManager) binding.list.getLayoutManager();
-			layoutManager.scrollToPositionWithOffset(scrollPosition.position, scrollPosition.offset);
-		}
-	}
+    @Override
+    public void onBackendConnected() {
+        refresh();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle bundle) {
+        super.onSaveInstanceState(bundle);
+        ScrollState scrollState = getScrollState();
+        if (scrollState != null) {
+            bundle.putParcelable(STATE_SCROLL_POSITION, scrollState);
+        }
+    }
+
+    private ScrollState getScrollState() {
+        if (this.binding == null) {
+            return null;
+        }
+        LinearLayoutManager layoutManager =
+                (LinearLayoutManager) this.binding.list.getLayoutManager();
+        int position = layoutManager.findFirstVisibleItemPosition();
+        final View view = this.binding.list.getChildAt(0);
+        if (view != null) {
+            return new ScrollState(position, view.getTop());
+        } else {
+            return new ScrollState(position, 0);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onStart()");
+        if (activity.xmppConnectionService != null) {
+            refresh();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.d(Config.LOGTAG, "ConversationsOverviewFragment.onResume()");
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem item) {
+        if (MenuDoubleTabUtil.shouldIgnoreTap()) {
+            return false;
+        }
+        switch (item.getItemId()) {
+            case R.id.action_search:
+                startActivity(new Intent(getActivity(), SearchActivity.class));
+                return true;
+            case R.id.action_easy_invite:
+                selectAccountToStartEasyInvite();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void selectAccountToStartEasyInvite() {
+        final List<Account> accounts =
+                EasyOnboardingInvite.getSupportingAccounts(activity.xmppConnectionService);
+        if (accounts.isEmpty()) {
+            // This can technically happen if opening the menu item races with accounts reconnecting
+            // or something
+            Toast.makeText(
+                            getActivity(),
+                            R.string.no_active_accounts_support_this,
+                            Toast.LENGTH_LONG)
+                    .show();
+        } else if (accounts.size() == 1) {
+            openEasyInviteScreen(accounts.get(0));
+        } else {
+            final AtomicReference<Account> selectedAccount = new AtomicReference<>(accounts.get(0));
+            final MaterialAlertDialogBuilder alertDialogBuilder =
+                    new MaterialAlertDialogBuilder(activity);
+            alertDialogBuilder.setTitle(R.string.choose_account);
+            final String[] asStrings =
+                    Collections2.transform(accounts, a -> a.getJid().asBareJid().toString())
+                            .toArray(new String[0]);
+            alertDialogBuilder.setSingleChoiceItems(
+                    asStrings, 0, (dialog, which) -> selectedAccount.set(accounts.get(which)));
+            alertDialogBuilder.setNegativeButton(R.string.cancel, null);
+            alertDialogBuilder.setPositiveButton(
+                    R.string.ok, (dialog, which) -> openEasyInviteScreen(selectedAccount.get()));
+            alertDialogBuilder.create().show();
+        }
+    }
+
+    private void openEasyInviteScreen(final Account account) {
+        EasyOnboardingInviteActivity.launch(account, activity);
+    }
+
+    @Override
+    void refresh() {
+        if (this.binding == null || this.activity == null) {
+            Log.d(
+                    Config.LOGTAG,
+                    "ConversationsOverviewFragment.refresh() skipped updated because view binding"
+                            + " or activity was null");
+            return;
+        }
+        this.activity.xmppConnectionService.populateWithOrderedConversations(this.conversations);
+        Conversation removed = this.swipedConversation.peek();
+        if (removed != null) {
+            if (removed.isRead()) {
+                this.conversations.remove(removed);
+            } else {
+                pendingActionHelper.execute();
+            }
+        }
+        this.conversationsAdapter.notifyDataSetChanged();
+        ScrollState scrollState = pendingScrollState.pop();
+        if (scrollState != null) {
+            setScrollPosition(scrollState);
+        }
+    }
+
+    private void setScrollPosition(ScrollState scrollPosition) {
+        if (scrollPosition != null) {
+            LinearLayoutManager layoutManager =
+                    (LinearLayoutManager) binding.list.getLayoutManager();
+            layoutManager.scrollToPositionWithOffset(
+                    scrollPosition.position, scrollPosition.offset);
+        }
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java b/src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java
index b20db451d..b12aba750 100644
--- a/src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java
@@ -11,18 +11,11 @@ import android.text.TextWatcher;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.Button;
-
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.databinding.DataBindingUtil;
 import androidx.fragment.app.DialogFragment;
-
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.DialogCreatePublicChannelBinding;
 import eu.siacs.conversations.entities.Account;
@@ -33,10 +26,14 @@ import eu.siacs.conversations.ui.util.DelayedHintHelper;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.XmppConnection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 public class CreatePublicChannelDialog extends DialogFragment implements OnBackendConnected {
 
-    private static final char[] FORBIDDEN = new char[]{'\u0022','&','\'','/',':','<','>','@'};
+    private static final char[] FORBIDDEN =
+            new char[] {'\u0022', '&', '\'', '/', ':', '<', '>', '@'};
 
     private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
     private CreatePublicChannelDialogListener mListener;
@@ -62,48 +59,56 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
     @NonNull
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        jidWasModified = savedInstanceState != null && savedInstanceState.getBoolean("jid_was_modified_false", false);
-        nameEntered = savedInstanceState != null && savedInstanceState.getBoolean("name_entered", false);
-        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
+        jidWasModified =
+                savedInstanceState != null
+                        && savedInstanceState.getBoolean("jid_was_modified_false", false);
+        nameEntered =
+                savedInstanceState != null && savedInstanceState.getBoolean("name_entered", false);
+        final MaterialAlertDialogBuilder builder =
+                new MaterialAlertDialogBuilder(requireActivity());
         builder.setTitle(R.string.create_public_channel);
-        final DialogCreatePublicChannelBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_create_public_channel, null, false);
-        binding.account.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-            @Override
-            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-                updateJidSuggestion(binding);
-            }
-
-            @Override
-            public void onNothingSelected(AdapterView<?> parent) {
-
-            }
-        });
-        binding.jid.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (skipTetxWatcher) {
-                    return;
-                }
-                if (jidWasModified) {
-                    jidWasModified = !TextUtils.isEmpty(s);
-                } else {
-                    jidWasModified = !s.toString().equals(getJidSuggestion(binding));
-                }
-            }
-        });
-        updateInputs(binding,false);
+        final DialogCreatePublicChannelBinding binding =
+                DataBindingUtil.inflate(
+                        getActivity().getLayoutInflater(),
+                        R.layout.dialog_create_public_channel,
+                        null,
+                        false);
+        binding.account.setOnItemSelectedListener(
+                new AdapterView.OnItemSelectedListener() {
+                    @Override
+                    public void onItemSelected(
+                            AdapterView<?> parent, View view, int position, long id) {
+                        updateJidSuggestion(binding);
+                    }
+
+                    @Override
+                    public void onNothingSelected(AdapterView<?> parent) {}
+                });
+        binding.jid.addTextChangedListener(
+                new TextWatcher() {
+                    @Override
+                    public void beforeTextChanged(
+                            CharSequence s, int start, int count, int after) {}
+
+                    @Override
+                    public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+                    @Override
+                    public void afterTextChanged(Editable s) {
+                        if (skipTetxWatcher) {
+                            return;
+                        }
+                        if (jidWasModified) {
+                            jidWasModified = !TextUtils.isEmpty(s);
+                        } else {
+                            jidWasModified = !s.toString().equals(getJidSuggestion(binding));
+                        }
+                    }
+                });
+        updateInputs(binding, false);
         ArrayList<String> mActivatedAccounts = getArguments().getStringArrayList(ACCOUNTS_LIST_KEY);
-        StartConversationActivity.populateAccountSpinner(getActivity(), mActivatedAccounts, binding.account);
+        StartConversationActivity.populateAccountSpinner(
+                getActivity(), mActivatedAccounts, binding.account);
         builder.setView(binding.getRoot());
         builder.setPositiveButton(nameEntered ? R.string.create : R.string.next, null);
         builder.setNegativeButton(nameEntered ? R.string.back : R.string.cancel, null);
@@ -111,14 +116,18 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
         this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
         binding.jid.setAdapter(knownHostsAdapter);
         final AlertDialog dialog = builder.create();
-        binding.groupChatName.setOnEditorActionListener((v, actionId, event) -> {
-            submit(dialog, binding);
-            return true;
-        });
-        dialog.setOnShowListener(dialogInterface -> {
-            dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> goBack(dialog, binding));
-            dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> submit(dialog, binding));
-        });
+        binding.groupChatName.setOnEditorActionListener(
+                (v, actionId, event) -> {
+                    submit(dialog, binding);
+                    return true;
+                });
+        dialog.setOnShowListener(
+                dialogInterface -> {
+                    dialog.getButton(DialogInterface.BUTTON_NEGATIVE)
+                            .setOnClickListener(v -> goBack(dialog, binding));
+                    dialog.getButton(DialogInterface.BUTTON_POSITIVE)
+                            .setOnClickListener(v -> submit(dialog, binding));
+                });
         return dialog;
     }
 
@@ -134,13 +143,15 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
-        outState.putBoolean("jid_was_modified",jidWasModified);
+        outState.putBoolean("jid_was_modified", jidWasModified);
         outState.putBoolean("name_entered", nameEntered);
         super.onSaveInstanceState(outState);
     }
 
     private static String getJidSuggestion(final DialogCreatePublicChannelBinding binding) {
-        final Account account = StartConversationActivity.getSelectedAccount(binding.getRoot().getContext(), binding.account);
+        final Account account =
+                StartConversationActivity.getSelectedAccount(
+                        binding.getRoot().getContext(), binding.account);
         final XmppConnection connection = account == null ? null : account.getXmppConnection();
         if (connection == null) {
             return "";
@@ -156,18 +167,18 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
             return "";
         } else {
             try {
-                return Jid.of(localpart, domain, null).toEscapedString();
+                return Jid.of(localpart, domain, null).toString();
             } catch (IllegalArgumentException e) {
-                return Jid.of(CryptoHelper.pronounceable(), domain, null).toEscapedString();
+                return Jid.of(CryptoHelper.pronounceable(), domain, null).toString();
             }
         }
     }
 
     private static String clean(String name) {
-        for(char c : FORBIDDEN) {
-            name = name.replace(String.valueOf(c),"");
+        for (char c : FORBIDDEN) {
+            name = name.replace(String.valueOf(c), "");
         }
-        return name.replaceAll("\\s+","-");
+        return name.replaceAll("\\s+", "-");
     }
 
     private void goBack(AlertDialog dialog, DialogCreatePublicChannelBinding binding) {
@@ -189,22 +200,26 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
         if (nameEntered) {
             binding.nameLayout.setError(null);
             if (address.isEmpty()) {
-                binding.xmppAddressLayout.setError(context.getText(R.string.please_enter_xmpp_address));
+                binding.xmppAddressLayout.setError(
+                        context.getText(R.string.please_enter_xmpp_address));
             } else {
                 final Jid jid;
                 try {
-                    jid = Jid.ofEscaped(address);
-                } catch (IllegalArgumentException e) {
+                    jid = Jid.ofUserInput(address);
+                } catch (final IllegalArgumentException e) {
                     binding.xmppAddressLayout.setError(context.getText(R.string.invalid_jid));
                     return;
                 }
-                final Account account = StartConversationActivity.getSelectedAccount(context, binding.account);
+                final Account account =
+                        StartConversationActivity.getSelectedAccount(context, binding.account);
                 if (account == null) {
                     return;
                 }
-                final XmppConnectionService service = ((XmppActivity )context).xmppConnectionService;
+                final XmppConnectionService service =
+                        ((XmppActivity) context).xmppConnectionService;
                 if (service != null && service.findFirstMuc(jid) != null) {
-                    binding.xmppAddressLayout.setError(context.getString(R.string.channel_already_exists));
+                    binding.xmppAddressLayout.setError(
+                            context.getString(R.string.channel_already_exists));
                     return;
                 }
                 mListener.onCreatePublicChannel(account, name, jid);
@@ -214,7 +229,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
             binding.xmppAddressLayout.setError(null);
             if (name.isEmpty()) {
                 binding.nameLayout.setError(context.getText(R.string.please_enter_name));
-            } else if (StartConversationActivity.isValidJid(name)){
+            } else if (StartConversationActivity.isValidJid(name)) {
                 binding.nameLayout.setError(context.getText(R.string.this_is_an_xmpp_address));
             } else {
                 binding.nameLayout.setError(null);
@@ -227,8 +242,8 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
         }
     }
 
-
-    private void updateInputs(final DialogCreatePublicChannelBinding binding, final boolean requestFocus) {
+    private void updateInputs(
+            final DialogCreatePublicChannelBinding binding, final boolean requestFocus) {
         binding.xmppAddressLayout.setVisibility(nameEntered ? View.VISIBLE : View.GONE);
         binding.nameLayout.setVisibility(nameEntered ? View.GONE : View.VISIBLE);
         if (!requestFocus) {
@@ -256,7 +271,8 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
     private void refreshKnownHosts() {
         Activity activity = getActivity();
         if (activity instanceof XmppActivity) {
-            Collection<String> hosts = ((XmppActivity) activity).xmppConnectionService.getKnownConferenceHosts();
+            Collection<String> hosts =
+                    ((XmppActivity) activity).xmppConnectionService.getKnownConferenceHosts();
             this.knownHostsAdapter.refresh(hosts);
         }
     }
@@ -271,8 +287,8 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
         try {
             mListener = (CreatePublicChannelDialogListener) context;
         } catch (ClassCastException e) {
-            throw new ClassCastException(context.toString()
-                    + " must implement CreateConferenceDialogListener");
+            throw new ClassCastException(
+                    context.toString() + " must implement CreateConferenceDialogListener");
         }
     }
 
@@ -280,7 +296,8 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
     public void onStart() {
         super.onStart();
         final Activity activity = getActivity();
-        if (activity instanceof XmppActivity && ((XmppActivity) activity).xmppConnectionService != null) {
+        if (activity instanceof XmppActivity
+                && ((XmppActivity) activity).xmppConnectionService != null) {
             refreshKnownHosts();
         }
     }
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index 61c76eb6e..14f732957 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -29,19 +29,16 @@ import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AlertDialog;
 import androidx.databinding.DataBindingUtil;
 import androidx.lifecycle.Lifecycle;
-
 import com.google.android.material.color.MaterialColors;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.textfield.TextInputLayout;
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Strings;
-
 import eu.siacs.conversations.AppSettings;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
@@ -79,15 +76,12 @@ import eu.siacs.conversations.xmpp.XmppConnection;
 import eu.siacs.conversations.xmpp.XmppConnection.Features;
 import eu.siacs.conversations.xmpp.forms.Data;
 import eu.siacs.conversations.xmpp.pep.Avatar;
-
-import okhttp3.HttpUrl;
-
-import org.openintents.openpgp.util.OpenPgpUtils;
-
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
+import okhttp3.HttpUrl;
+import org.openintents.openpgp.util.OpenPgpUtils;
 
 public class EditAccountActivity extends OmemoActivity
         implements OnAccountUpdate,
@@ -144,8 +138,7 @@ public class EditAccountActivity extends OmemoActivity
                                 new Intent(
                                         getApplicationContext(),
                                         PublishProfilePictureActivity.class);
-                        intent.putExtra(
-                                EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toEscapedString());
+                        intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString());
                         startActivity(intent);
                     }
                 }
@@ -259,12 +252,12 @@ public class EditAccountActivity extends OmemoActivity
                     try {
                         if (mUsernameMode) {
                             jid =
-                                    Jid.ofEscaped(
+                                    Jid.of(
                                             binding.accountJid.getText().toString(),
                                             getUserModeDomain(),
                                             null);
                         } else {
-                            jid = Jid.ofEscaped(binding.accountJid.getText().toString());
+                            jid = Jid.ofUserInput(binding.accountJid.getText().toString());
                             Resolver.checkDomain(jid);
                         }
                     } catch (final NullPointerException | IllegalArgumentException e) {
@@ -539,15 +532,13 @@ public class EditAccountActivity extends OmemoActivity
                         if (wasFirstAccount) {
                             intent.putExtra("init", true);
                         }
-                        intent.putExtra(
-                                EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toEscapedString());
+                        intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString());
                     } else {
                         intent =
                                 new Intent(
                                         getApplicationContext(),
                                         PublishProfilePictureActivity.class);
-                        intent.putExtra(
-                                EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toEscapedString());
+                        intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().asBareJid().toString());
                         intent.putExtra("setup", true);
                     }
                     if (wasFirstAccount) {
@@ -700,9 +691,9 @@ public class EditAccountActivity extends OmemoActivity
     protected boolean jidEdited() {
         final String unmodified;
         if (mUsernameMode) {
-            unmodified = this.mAccount.getJid().getEscapedLocal();
+            unmodified = this.mAccount.getJid().getLocal();
         } else {
-            unmodified = this.mAccount.getJid().asBareJid().toEscapedString();
+            unmodified = this.mAccount.getJid().asBareJid().toString();
         }
         return !unmodified.equals(this.binding.accountJid.getText().toString());
     }
@@ -824,7 +815,7 @@ public class EditAccountActivity extends OmemoActivity
         final Intent intent = getIntent();
         if (intent != null) {
             try {
-                this.jidToEdit = Jid.ofEscaped(intent.getStringExtra("jid"));
+                this.jidToEdit = Jid.of(intent.getStringExtra("jid"));
             } catch (final IllegalArgumentException | NullPointerException ignored) {
                 this.jidToEdit = null;
             }
@@ -929,8 +920,7 @@ public class EditAccountActivity extends OmemoActivity
     @Override
     public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
         if (mAccount != null) {
-            savedInstanceState.putString(
-                    "account", mAccount.getJid().asBareJid().toEscapedString());
+            savedInstanceState.putString("account", mAccount.getJid().asBareJid().toString());
             savedInstanceState.putBoolean("initMode", mInitMode);
             savedInstanceState.putBoolean(
                     "showMoreTable", binding.serverInfoMore.getVisibility() == View.VISIBLE);
@@ -943,8 +933,7 @@ public class EditAccountActivity extends OmemoActivity
         if (mSavedInstanceAccount != null) {
             try {
                 this.mAccount =
-                        xmppConnectionService.findAccountByJid(
-                                Jid.ofEscaped(mSavedInstanceAccount));
+                        xmppConnectionService.findAccountByJid(Jid.of(mSavedInstanceAccount));
                 this.mInitMode = mSavedInstanceInit;
                 init = false;
             } catch (IllegalArgumentException e) {
@@ -1010,7 +999,7 @@ public class EditAccountActivity extends OmemoActivity
                 break;
             case R.id.action_show_block_list:
                 final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
-                showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toEscapedString());
+                showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
                 startActivity(showBlocklistIntent);
                 break;
             case R.id.action_server_info_show_more:
@@ -1070,7 +1059,7 @@ public class EditAccountActivity extends OmemoActivity
 
     private void gotoChangePassword() {
         final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
-        changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toEscapedString());
+        changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
         startActivity(changePasswordIntent);
     }
 
@@ -1167,15 +1156,12 @@ public class EditAccountActivity extends OmemoActivity
         if (init) {
             this.binding.accountJid.getEditableText().clear();
             if (mUsernameMode) {
-                this.binding
-                        .accountJid
-                        .getEditableText()
-                        .append(this.mAccount.getJid().getEscapedLocal());
+                this.binding.accountJid.getEditableText().append(this.mAccount.getJid().getLocal());
             } else {
                 this.binding
                         .accountJid
                         .getEditableText()
-                        .append(this.mAccount.getJid().asBareJid().toEscapedString());
+                        .append(this.mAccount.getJid().asBareJid().toString());
             }
             this.binding.accountPassword.getEditableText().clear();
             this.binding.accountPassword.getEditableText().append(this.mAccount.getPassword());
diff --git a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
index 787203f0c..bcd2432da 100644
--- a/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java
@@ -7,21 +7,12 @@ import android.text.Editable;
 import android.text.TextWatcher;
 import android.view.View;
 import android.widget.ArrayAdapter;
-
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.databinding.DataBindingUtil;
 import androidx.fragment.app.DialogFragment;
-
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.common.base.Strings;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.DialogEnterJidBinding;
 import eu.siacs.conversations.services.XmppConnectionService;
@@ -29,6 +20,11 @@ import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 import eu.siacs.conversations.ui.util.DelayedHintHelper;
 import eu.siacs.conversations.xmpp.Jid;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 public class EnterJidDialog extends DialogFragment implements OnBackendConnected, TextWatcher {
 
@@ -95,10 +91,15 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
     @Override
     public Dialog onCreateDialog(final Bundle savedInstanceState) {
         final var arguments = getArguments();
-        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
+        final MaterialAlertDialogBuilder builder =
+                new MaterialAlertDialogBuilder(requireActivity());
         builder.setTitle(arguments.getString(TITLE_KEY));
         binding =
-                DataBindingUtil.inflate(requireActivity().getLayoutInflater(), R.layout.dialog_enter_jid, null, false);
+                DataBindingUtil.inflate(
+                        requireActivity().getLayoutInflater(),
+                        R.layout.dialog_enter_jid,
+                        null,
+                        false);
         this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
         binding.jid.setAdapter(this.knownHostsAdapter);
         binding.jid.addTextChangedListener(this);
@@ -124,7 +125,8 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                     binding.account);
         } else {
             final ArrayAdapter<String> adapter =
-                    new ArrayAdapter<>(requireActivity(), R.layout.item_autocomplete, new String[] {account});
+                    new ArrayAdapter<>(
+                            requireActivity(), R.layout.item_autocomplete, new String[] {account});
             binding.account.setText(account);
             binding.account.setEnabled(false);
             adapter.setDropDownViewResource(R.layout.item_autocomplete);
@@ -136,8 +138,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
         builder.setPositiveButton(getArguments().getString(POSITIVE_BUTTON_KEY), null);
         this.dialog = builder.create();
 
-        View.OnClickListener dialogOnClick =
-                v -> handleEnter(binding, account);
+        View.OnClickListener dialogOnClick = v -> handleEnter(binding, account);
 
         binding.jid.setOnEditorActionListener(
                 (v, actionId, event) -> {
@@ -156,13 +157,13 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
             return;
         }
         try {
-                accountJid = Jid.ofEscaped((String) binding.account.getEditableText().toString());
+            accountJid = Jid.of(binding.account.getEditableText().toString());
         } catch (final IllegalArgumentException e) {
             return;
         }
         final Jid contactJid;
         try {
-            contactJid = Jid.ofEscaped(binding.jid.getText().toString().trim());
+            contactJid = Jid.ofUserInput(binding.jid.getText().toString().trim());
         } catch (final IllegalArgumentException e) {
             binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
             return;
@@ -176,7 +177,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                 issuedWarning = true;
                 return;
             }
-            if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
+            if (suspiciousSubDomain(contactJid.getDomain().toString())) {
                 binding.jidLayout.setError(
                         getActivity().getString(R.string.this_looks_like_channel));
                 dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
diff --git a/src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java b/src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java
index ac5b07e77..58d46fd76 100644
--- a/src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/MediaBrowserActivity.java
@@ -3,11 +3,7 @@ package eu.siacs.conversations.ui;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-
 import androidx.databinding.DataBindingUtil;
-
-import java.util.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityMediaBrowserBinding;
 import eu.siacs.conversations.entities.Account;
@@ -18,6 +14,7 @@ import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
 import eu.siacs.conversations.ui.util.Attachment;
 import eu.siacs.conversations.ui.util.GridManager;
 import eu.siacs.conversations.xmpp.Jid;
+import java.util.List;
 
 public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded {
 
@@ -28,20 +25,17 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        this.binding = DataBindingUtil.setContentView(this,R.layout.activity_media_browser);
+        this.binding = DataBindingUtil.setContentView(this, R.layout.activity_media_browser);
         Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
         setSupportActionBar(binding.toolbar);
         configureActionBar(getSupportActionBar());
         mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);
         this.binding.media.setAdapter(mMediaAdapter);
         GridManager.setupLayoutManager(this, this.binding.media, R.dimen.browser_media_size);
-
     }
 
     @Override
-    protected void refreshUiReal() {
-
-    }
+    protected void refreshUiReal() {}
 
     @Override
     protected void onBackendConnected() {
@@ -49,29 +43,30 @@ public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded
         String account = intent == null ? null : intent.getStringExtra("account");
         String jid = intent == null ? null : intent.getStringExtra("jid");
         if (account != null && jid != null) {
-            xmppConnectionService.getAttachments(account, Jid.ofEscaped(jid), 0, this);
+            xmppConnectionService.getAttachments(account, Jid.of(jid), 0, this);
         }
     }
 
     public static void launch(Context context, Contact contact) {
-        launch(context, contact.getAccount(), contact.getJid().asBareJid().toEscapedString());
+        launch(context, contact.getAccount(), contact.getJid().asBareJid().toString());
     }
 
     public static void launch(Context context, Conversation conversation) {
-        launch(context, conversation.getAccount(), conversation.getJid().asBareJid().toEscapedString());
+        launch(context, conversation.getAccount(), conversation.getJid().asBareJid().toString());
     }
 
     private static void launch(Context context, Account account, String jid) {
         final Intent intent = new Intent(context, MediaBrowserActivity.class);
-        intent.putExtra("account",account.getUuid());
-        intent.putExtra("jid",jid);
+        intent.putExtra("account", account.getUuid());
+        intent.putExtra("jid", jid);
         context.startActivity(intent);
     }
 
     @Override
     public void onMediaLoaded(List<Attachment> attachments) {
-        runOnUiThread(()->{
-            mMediaAdapter.setAttachments(attachments);
-        });
+        runOnUiThread(
+                () -> {
+                    mMediaAdapter.setAttachments(attachments);
+                });
     }
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
index dafbbabfc..ebc8225d8 100644
--- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java
@@ -60,8 +60,7 @@ public class PublishProfilePictureActivity extends XmppActivity
                                         getApplicationContext(), StartConversationActivity.class);
                         StartConversationActivity.addInviteUri(intent, getIntent());
                         intent.putExtra("init", true);
-                        intent.putExtra(
-                                EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
+                        intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
                         startActivity(intent);
                     }
                     Toast.makeText(
@@ -118,8 +117,7 @@ public class PublishProfilePictureActivity extends XmppActivity
                         }
                         StartConversationActivity.addInviteUri(intent, getIntent());
                         if (account != null) {
-                            intent.putExtra(
-                                    EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
+                            intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
                         }
                         startActivity(intent);
                     }
diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
index 89068306d..8435a4da1 100644
--- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
@@ -1,7 +1,6 @@
 package eu.siacs.conversations.ui;
 
 import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
-
 import static java.util.Arrays.asList;
 
 import android.Manifest;
@@ -25,13 +24,11 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.StringRes;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -41,7 +38,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityRtpSessionBinding;
@@ -65,16 +61,14 @@ import eu.siacs.conversations.xmpp.jingle.Media;
 import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
 import eu.siacs.conversations.xmpp.jingle.RtpCapability;
 import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
-
-import org.webrtc.RendererCommon;
-import org.webrtc.SurfaceViewRenderer;
-import org.webrtc.VideoTrack;
-
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import org.webrtc.RendererCommon;
+import org.webrtc.SurfaceViewRenderer;
+import org.webrtc.VideoTrack;
 
 public class RtpSessionActivity extends XmppActivity
         implements XmppConnectionService.OnJingleRtpConnectionUpdate,
@@ -300,7 +294,7 @@ public class RtpSessionActivity extends XmppActivity
         final String action = intent.getAction();
         final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION);
         final Account account = extractAccount(intent);
-        final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
+        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
         final String state = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE);
         if (!Intent.ACTION_VIEW.equals(action)
                 || state == null
@@ -504,7 +498,7 @@ public class RtpSessionActivity extends XmppActivity
         Log.d(Config.LOGTAG, "initializeWithIntent(" + event + "," + action + ")");
         final Account account = extractAccount(intent);
         final var extraWith = intent.getStringExtra(EXTRA_WITH);
-        final Jid with = Strings.isNullOrEmpty(extraWith) ? null : Jid.ofEscaped(extraWith);
+        final Jid with = Strings.isNullOrEmpty(extraWith) ? null : Jid.of(extraWith);
         if (with == null || account == null) {
             Log.e(Config.LOGTAG, "intent is missing extras (account or with)");
             return;
@@ -573,7 +567,7 @@ public class RtpSessionActivity extends XmppActivity
         binding.with.setText(contact.getDisplayName());
         if (Arrays.asList(RtpEndUserState.INCOMING_CALL, RtpEndUserState.ACCEPTING_CALL)
                 .contains(state)) {
-            binding.withJid.setText(contact.getJid().asBareJid().toEscapedString());
+            binding.withJid.setText(contact.getJid().asBareJid().toString());
             binding.withJid.setVisibility(View.VISIBLE);
         } else {
             binding.withJid.setVisibility(View.GONE);
@@ -776,7 +770,8 @@ public class RtpSessionActivity extends XmppActivity
                             .getTerminalSessionState(with, sessionId);
             if (terminatedRtpSession == null) {
                 throw new IllegalStateException(
-                        "failed to initialize activity with running rtp session. session not found");
+                        "failed to initialize activity with running rtp session. session not"
+                                + " found");
             }
             initializeWithTerminatedSessionState(account, with, terminatedRtpSession);
             return true;
@@ -837,8 +832,8 @@ public class RtpSessionActivity extends XmppActivity
 
     private void resetIntent(final Account account, final Jid with, final String sessionId) {
         final Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
-        intent.putExtra(EXTRA_WITH, with.toEscapedString());
+        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
+        intent.putExtra(EXTRA_WITH, with.toString());
         intent.putExtra(EXTRA_SESSION_ID, sessionId);
         setIntent(intent);
     }
@@ -895,10 +890,12 @@ public class RtpSessionActivity extends XmppActivity
             case RETRACTED -> setTitle(R.string.rtp_state_retracted);
             case APPLICATION_ERROR -> setTitle(R.string.rtp_state_application_failure);
             case SECURITY_ERROR -> setTitle(R.string.rtp_state_security_error);
-            case ENDED -> throw new IllegalStateException(
-                    "Activity should have called finishAndReleaseWakeLock();");
-            default -> throw new IllegalStateException(
-                    String.format("State %s has not been handled in UI", state));
+            case ENDED ->
+                    throw new IllegalStateException(
+                            "Activity should have called finishAndReleaseWakeLock();");
+            default ->
+                    throw new IllegalStateException(
+                            String.format("State %s has not been handled in UI", state));
         }
     }
 
@@ -932,9 +929,7 @@ public class RtpSessionActivity extends XmppActivity
             final Account account = contact == null ? getWith().getAccount() : contact.getAccount();
             binding.usingAccount.setVisibility(View.VISIBLE);
             binding.usingAccount.setText(
-                    getString(
-                            R.string.using_account,
-                            account.getJid().asBareJid().toEscapedString()));
+                    getString(R.string.using_account, account.getJid().asBareJid().toString()));
         } else {
             binding.usingAccount.setVisibility(View.GONE);
             binding.contactPhoto.setVisibility(View.GONE);
@@ -1430,12 +1425,12 @@ public class RtpSessionActivity extends XmppActivity
     private void retry(final View view) {
         final Intent intent = getIntent();
         final Account account = extractAccount(intent);
-        final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
+        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
         final String lastAction = intent.getStringExtra(EXTRA_LAST_ACTION);
         final String action = intent.getAction();
         final Set<Media> media = actionToMedia(lastAction == null ? action : lastAction);
         this.rtpConnectionReference = null;
-        Log.d(Config.LOGTAG, "attempting retry with " + with.toEscapedString());
+        Log.d(Config.LOGTAG, "attempting retry with " + with.toString());
         CallIntegrationConnectionService.placeCall(xmppConnectionService, account, with, media);
     }
 
@@ -1446,7 +1441,7 @@ public class RtpSessionActivity extends XmppActivity
     private void recordVoiceMail(final View view) {
         final Intent intent = getIntent();
         final Account account = extractAccount(intent);
-        final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
+        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
         final Conversation conversation =
                 xmppConnectionService.findOrCreateConversation(account, with, false, true);
         final Intent launchIntent = new Intent(this, ConversationsActivity.class);
@@ -1611,7 +1606,7 @@ public class RtpSessionActivity extends XmppActivity
             return;
         }
         final Set<Media> media = actionToMedia(currentIntent.getStringExtra(EXTRA_LAST_ACTION));
-        if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) {
+        if (Jid.of(withExtra).asBareJid().equals(with)) {
             runOnUiThread(
                     () -> {
                         updateVerifiedShield(false);
@@ -1634,11 +1629,11 @@ public class RtpSessionActivity extends XmppActivity
     private void resetIntent(
             final Account account, Jid with, final RtpEndUserState state, final Set<Media> media) {
         final Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
+        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
         if (RtpCapability.jmiSupport(account.getRoster().getContact(with))) {
-            intent.putExtra(EXTRA_WITH, with.asBareJid().toEscapedString());
+            intent.putExtra(EXTRA_WITH, with.asBareJid().toString());
         } else {
-            intent.putExtra(EXTRA_WITH, with.toEscapedString());
+            intent.putExtra(EXTRA_WITH, with.toString());
         }
         intent.putExtra(EXTRA_LAST_REPORTED_STATE, state.toString());
         intent.putExtra(
diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
index 1f83d1f45..a081f3755 100644
--- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java
@@ -216,7 +216,7 @@ public class ShareWithActivity extends XmppActivity
         final Conversation conversation;
         Account account;
         try {
-            account = xmppConnectionService.findAccountByJid(Jid.ofEscaped(share.account));
+            account = xmppConnectionService.findAccountByJid(Jid.of(share.account));
         } catch (final IllegalArgumentException e) {
             account = null;
         }
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index a8a0cb058..a2ec5fe94 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -36,7 +36,6 @@ import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
-
 import androidx.annotation.MenuRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -52,7 +51,6 @@ import androidx.fragment.app.FragmentTransaction;
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
-
 import com.google.android.material.color.MaterialColors;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.textfield.TextInputLayout;
@@ -60,7 +58,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.leinardi.android.speeddial.SpeedDialActionItem;
 import com.leinardi.android.speeddial.SpeedDialView;
-
 import eu.siacs.conversations.BuildConfig;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
@@ -87,7 +84,6 @@ import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
 import eu.siacs.conversations.xmpp.XmppConnection;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -359,7 +355,7 @@ public class StartConversationActivity extends XmppActivity
                             mSearchEditText != null ? mSearchEditText.getText().toString() : null;
                     final String prefilled;
                     if (isValidJid(searchString)) {
-                        prefilled = Jid.ofEscaped(searchString).toEscapedString();
+                        prefilled = Jid.of(searchString).toString();
                     } else {
                         prefilled = null;
                     }
@@ -418,14 +414,15 @@ public class StartConversationActivity extends XmppActivity
                             .create();
             speedDialView.addActionItem(actionItem);
         }
-        speedDialView.setContentDescription(getString(R.string.add_contact_or_create_or_join_group_chat));
+        speedDialView.setContentDescription(
+                getString(R.string.add_contact_or_create_or_join_group_chat));
     }
 
-    public static boolean isValidJid(String input) {
+    public static boolean isValidJid(final String input) {
         try {
-            Jid jid = Jid.ofEscaped(input);
+            final Jid jid = Jid.ofUserInput(input);
             return !jid.isDomainJid();
-        } catch (IllegalArgumentException e) {
+        } catch (final IllegalArgumentException e) {
             return false;
         }
     }
@@ -505,7 +502,7 @@ public class StartConversationActivity extends XmppActivity
 
     protected void shareBookmarkUri(int position) {
         Bookmark bookmark = (Bookmark) conferences.get(position);
-        shareAsChannel(this, bookmark.getJid().asBareJid().toEscapedString());
+        shareAsChannel(this, bookmark.getJid().asBareJid().toString());
     }
 
     public static void shareAsChannel(final Context context, final String address) {
@@ -549,7 +546,7 @@ public class StartConversationActivity extends XmppActivity
     protected void showQrForContact() {
         int position = contact_context_id;
         Contact contact = (Contact) contacts.get(position);
-        showQrCode("xmpp:" + contact.getJid().asBareJid().toEscapedString());
+        showQrCode("xmpp:" + contact.getJid().asBareJid().toString());
     }
 
     protected void toggleContactBlock() {
@@ -564,8 +561,7 @@ public class StartConversationActivity extends XmppActivity
         builder.setNegativeButton(R.string.cancel, null);
         builder.setTitle(R.string.action_delete_contact);
         builder.setMessage(
-                JidDialog.style(
-                        this, R.string.remove_contact_text, contact.getJid().toEscapedString()));
+                JidDialog.style(this, R.string.remove_contact_text, contact.getJid().toString()));
         builder.setPositiveButton(
                 R.string.delete,
                 (dialog, which) -> {
@@ -588,11 +584,10 @@ public class StartConversationActivity extends XmppActivity
                     JidDialog.style(
                             this,
                             R.string.remove_bookmark_and_close,
-                            bookmark.getJid().toEscapedString()));
+                            bookmark.getJid().toString()));
         } else {
             builder.setMessage(
-                    JidDialog.style(
-                            this, R.string.remove_bookmark, bookmark.getJid().toEscapedString()));
+                    JidDialog.style(this, R.string.remove_bookmark, bookmark.getJid().toString()));
         }
         builder.setPositiveButton(
                 hasConversation ? R.string.delete_and_close : R.string.delete,
@@ -710,7 +705,7 @@ public class StartConversationActivity extends XmppActivity
         if (context instanceof XmppActivity) {
             final Jid jid;
             try {
-                jid = Jid.ofEscaped(spinner.getText().toString());
+                jid = Jid.of(spinner.getText().toString());
             } catch (final IllegalArgumentException e) {
                 return null;
             }
@@ -1078,11 +1073,11 @@ public class StartConversationActivity extends XmppActivity
                 switchToConversationDoNotAppend(muc, invite.getBody());
                 return true;
             } else {
-                showJoinConferenceDialog(invite.getJid().asBareJid().toEscapedString());
+                showJoinConferenceDialog(invite.getJid().asBareJid().toString());
                 return false;
             }
-        } else if (contacts.size() == 0) {
-            showCreateContactDialog(invite.getJid().toEscapedString(), invite);
+        } else if (contacts.isEmpty()) {
+            showCreateContactDialog(invite.getJid().toString(), invite);
             return false;
         } else if (contacts.size() == 1) {
             Contact contact = contacts.get(0);
@@ -1106,10 +1101,10 @@ public class StartConversationActivity extends XmppActivity
             if (mMenuSearchView != null) {
                 mMenuSearchView.expandActionView();
                 mSearchEditText.setText("");
-                mSearchEditText.append(invite.getJid().toEscapedString());
-                filter(invite.getJid().toEscapedString());
+                mSearchEditText.append(invite.getJid().toString());
+                filter(invite.getJid().toString());
             } else {
-                mInitialSearchValue.push(invite.getJid().toEscapedString());
+                mInitialSearchValue.push(invite.getJid().toString());
             }
             return true;
         }
@@ -1125,7 +1120,7 @@ public class StartConversationActivity extends XmppActivity
                 JidDialog.style(
                         this,
                         R.string.verifying_omemo_keys_trusted_source,
-                        contact.getJid().asBareJid().toEscapedString(),
+                        contact.getJid().asBareJid().toString(),
                         contact.getDisplayName()));
         builder.setView(view);
         builder.setPositiveButton(
@@ -1237,8 +1232,7 @@ public class StartConversationActivity extends XmppActivity
         intent.putExtra(ChooseContactActivity.EXTRA_SELECT_MULTIPLE, true);
         intent.putExtra(ChooseContactActivity.EXTRA_GROUP_CHAT_NAME, name.trim());
         intent.putExtra(
-                ChooseContactActivity.EXTRA_ACCOUNT,
-                account.getJid().asBareJid().toEscapedString());
+                ChooseContactActivity.EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
         intent.putExtra(ChooseContactActivity.EXTRA_TITLE_RES_ID, R.string.choose_participants);
         startActivityForResult(intent, REQUEST_CREATE_CONFERENCE);
     }
@@ -1259,13 +1253,13 @@ public class StartConversationActivity extends XmppActivity
         final String input = jid.getText().toString().trim();
         Jid conferenceJid;
         try {
-            conferenceJid = Jid.ofEscaped(input);
+            conferenceJid = Jid.ofUserInput(input);
         } catch (final IllegalArgumentException e) {
             final XmppUri xmppUri = new XmppUri(input);
             if (xmppUri.isValidJid() && xmppUri.isAction(XmppUri.ACTION_JOIN)) {
                 final Editable editable = jid.getEditableText();
                 editable.clear();
-                editable.append(xmppUri.getJid().toEscapedString());
+                editable.append(xmppUri.getJid().toString());
                 conferenceJid = xmppUri.getJid();
             } else {
                 layout.setError(getString(R.string.invalid_jid));
diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
index ba069b9a5..c1a5ef08c 100644
--- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java
@@ -1,6 +1,5 @@
 package eu.siacs.conversations.ui;
 
-import android.app.AlertDialog;
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
@@ -10,12 +9,9 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Toast;
-
 import androidx.appcompat.app.ActionBar;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.OmemoSetting;
@@ -32,429 +28,515 @@ import eu.siacs.conversations.utils.IrregularUnicodeDetector;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
-
-import org.whispersystems.libsignal.IdentityKey;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import org.whispersystems.libsignal.IdentityKey;
 
 public class TrustKeysActivity extends OmemoActivity implements OnKeyStatusUpdated {
-	private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
-	private final Map<Jid, Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
-	private final OnClickListener mCancelButtonListener = v -> {
-		setResult(RESULT_CANCELED);
-		finish();
-	};
-	private List<Jid> contactJids;
-	private Account mAccount;
-	private Conversation mConversation;
-	private final OnClickListener mSaveButtonListener = v -> {
-		commitTrusts();
-		finishOk(false);
-	};
-	private final AtomicBoolean mUseCameraHintShown = new AtomicBoolean(false);
-	private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
-	private Toast mUseCameraHintToast = null;
-	private ActivityTrustKeysBinding binding;
-
-	@Override
-	protected void refreshUiReal() {
-		invalidateOptionsMenu();
-		populateView();
-	}
-
-	@Override
-	protected void onCreate(final Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		this.binding = DataBindingUtil.setContentView(this, R.layout.activity_trust_keys);
-		Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
-		this.contactJids = new ArrayList<>();
-		final var intent = getIntent();
-		final String[] contacts = intent == null ? null : intent.getStringArrayExtra("contacts");
-		for (final String jid : (contacts == null ? new String[0] : contacts)) {
-			try {
-				this.contactJids.add(Jid.of(jid));
-			} catch (final IllegalArgumentException ignored) {
-			}
-		}
-
-		binding.cancelButton.setOnClickListener(mCancelButtonListener);
-		binding.saveButton.setOnClickListener(mSaveButtonListener);
-
-		setSupportActionBar(binding.toolbar);
-		configureActionBar(getSupportActionBar());
-
-		if (savedInstanceState != null) {
-			mUseCameraHintShown.set(savedInstanceState.getBoolean("camera_hint_shown", false));
-		}
-	}
-
-	@Override
-	public void onSaveInstanceState(Bundle savedInstanceState) {
-		savedInstanceState.putBoolean("camera_hint_shown", mUseCameraHintShown.get());
-		super.onSaveInstanceState(savedInstanceState);
-	}
-
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		getMenuInflater().inflate(R.menu.trust_keys, menu);
-		MenuItem scanQrCode = menu.findItem(R.id.action_scan_qr_code);
-		scanQrCode.setVisible((!ownKeysToTrust.isEmpty() || foreignActuallyHasKeys()) && isCameraFeatureAvailable());
-		return super.onCreateOptionsMenu(menu);
-	}
-
-	private void showCameraToast() {
-		mUseCameraHintToast = Toast.makeText(this, R.string.use_camera_icon_to_scan_barcode, Toast.LENGTH_LONG);
-		ActionBar actionBar = getSupportActionBar();
-		mUseCameraHintToast.setGravity(Gravity.TOP | Gravity.END, 0, actionBar == null ? 0 : actionBar.getHeight());
-		mUseCameraHintToast.show();
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item) {
-		switch (item.getItemId()) {
-			case R.id.action_scan_qr_code:
-				if (hasPendingKeyFetches()) {
-					Toast.makeText(this, R.string.please_wait_for_keys_to_be_fetched, Toast.LENGTH_SHORT).show();
-				} else {
-					ScanActivity.scan(this);
-					//new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE"));
-					return true;
-				}
-		}
-		return super.onOptionsItemSelected(item);
-	}
-
-	@Override
-	protected void onStop() {
-		super.onStop();
-		if (mUseCameraHintToast != null) {
-			mUseCameraHintToast.cancel();
-		}
-	}
-
-	@Override
-	protected void processFingerprintVerification(XmppUri uri) {
-		if (mConversation != null
-				&& mAccount != null
-				&& uri.hasFingerprints()
-				&& mAccount.getAxolotlService().getCryptoTargets(mConversation).contains(uri.getJid())) {
-			boolean performedVerification = xmppConnectionService.verifyFingerprints(mAccount.getRoster().getContact(uri.getJid()), uri.getFingerprints());
-			boolean keys = reloadFingerprints();
-			if (performedVerification && !keys && !hasNoOtherTrustedKeys() && !hasPendingKeyFetches()) {
-				Toast.makeText(this, R.string.all_omemo_keys_have_been_verified, Toast.LENGTH_SHORT).show();
-				finishOk(false);
-				return;
-			} else if (performedVerification) {
-				Toast.makeText(this, R.string.verified_fingerprints, Toast.LENGTH_SHORT).show();
-			}
-		} else {
-			reloadFingerprints();
-			Log.d(Config.LOGTAG, "xmpp uri was: " + uri.getJid() + " has Fingerprints: " + uri.hasFingerprints());
-			Toast.makeText(this, R.string.barcode_does_not_contain_fingerprints_for_this_chat, Toast.LENGTH_SHORT).show();
-		}
-		populateView();
-	}
-
-	private void populateView() {
-		setTitle(getString(R.string.trust_omemo_fingerprints));
-		binding.ownKeysDetails.removeAllViews();
-		binding.foreignKeys.removeAllViews();
-		boolean hasOwnKeys = false;
-		boolean hasForeignKeys = false;
-		for (final String fingerprint : ownKeysToTrust.keySet()) {
-			hasOwnKeys = true;
-			addFingerprintRowWithListeners(binding.ownKeysDetails, mAccount, fingerprint, false,
-					FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)), false, false,
-					(buttonView, isChecked) -> {
-						ownKeysToTrust.put(fingerprint, isChecked);
-						// own fingerprints have no impact on locked status.
-					}
-			);
-		}
-
-		synchronized (this.foreignKeysToTrust) {
-			for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
-				hasForeignKeys = true;
-				KeysCardBinding keysCardBinding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.keys_card, binding.foreignKeys, false);
-				final Jid jid = entry.getKey();
-				keysCardBinding.foreignKeysTitle.setText(IrregularUnicodeDetector.style(this, jid));
-				keysCardBinding.foreignKeysTitle.setOnClickListener(v -> switchToContactDetails(mAccount.getRoster().getContact(jid)));
-				final Map<String, Boolean> fingerprints = entry.getValue();
-				for (final String fingerprint : fingerprints.keySet()) {
-					addFingerprintRowWithListeners(keysCardBinding.foreignKeysDetails, mAccount, fingerprint, false,
-							FingerprintStatus.createActive(fingerprints.get(fingerprint)), false, false,
-							(buttonView, isChecked) -> {
-								fingerprints.put(fingerprint, isChecked);
-								lockOrUnlockAsNeeded();
-							}
-					);
-				}
-				if (fingerprints.isEmpty()) {
-					keysCardBinding.noKeysToAccept.setVisibility(View.VISIBLE);
-					if (hasNoOtherTrustedKeys(jid)) {
-						if (!mAccount.getRoster().getContact(jid).mutualPresenceSubscription()) {
-							keysCardBinding.noKeysToAccept.setText(R.string.error_no_keys_to_trust_presence);
-						} else {
-							keysCardBinding.noKeysToAccept.setText(R.string.error_no_keys_to_trust_server_error);
-						}
-					} else {
-						keysCardBinding.noKeysToAccept.setText(getString(R.string.no_keys_just_confirm, mAccount.getRoster().getContact(jid).getDisplayName()));
-					}
-				} else {
-					keysCardBinding.noKeysToAccept.setVisibility(View.GONE);
-				}
-				binding.foreignKeys.addView(keysCardBinding.foreignKeysCard);
-			}
-		}
-
-		if ((hasOwnKeys || foreignActuallyHasKeys()) && isCameraFeatureAvailable() && mUseCameraHintShown.compareAndSet(false, true)) {
-			showCameraToast();
-		}
-
-		binding.ownKeysTitle.setText(mAccount.getJid().asBareJid().toEscapedString());
-		binding.ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
-		binding.foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
-		if (hasPendingKeyFetches()) {
-			setFetching();
-			lock();
-		} else {
-			if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
-				binding.keyErrorMessageCard.setVisibility(View.VISIBLE);
-				boolean lastReportWasError = lastFetchReport == AxolotlService.FetchStatus.ERROR;
-				boolean errorFetchingBundle = mAccount.getAxolotlService().fetchMapHasErrors(contactJids);
-				boolean errorFetchingDeviceList = mAccount.getAxolotlService().hasErrorFetchingDeviceList(contactJids);
-				boolean anyWithoutMutualPresenceSubscription = anyWithoutMutualPresenceSubscription(contactJids);
-				if (errorFetchingDeviceList) {
-					binding.keyErrorMessage.setVisibility(View.VISIBLE);
-					binding.keyErrorMessage.setText(R.string.error_trustkey_device_list);
-				} else if (errorFetchingBundle || lastReportWasError) {
-					binding.keyErrorMessage.setVisibility(View.VISIBLE);
-					binding.keyErrorMessage.setText(R.string.error_trustkey_bundle);
-				} else {
-					binding.keyErrorMessage.setVisibility(View.GONE);
-				}
-				this.binding.keyErrorHintMutual.setVisibility(anyWithoutMutualPresenceSubscription ? View.VISIBLE : View.GONE);
-				Contact contact = mAccount.getRoster().getContact(contactJids.get(0));
-				binding.keyErrorGeneral.setText(getString(R.string.error_trustkey_general, getString(R.string.app_name), contact.getDisplayName()));
-				binding.ownKeysDetails.removeAllViews();
-				if (OmemoSetting.isAlways()) {
-					binding.disableButton.setVisibility(View.GONE);
-				} else {
-					binding.disableButton.setVisibility(View.VISIBLE);
-					binding.disableButton.setOnClickListener(this::disableEncryptionDialog);
-				}
-				binding.ownKeysCard.setVisibility(View.GONE);
-				binding.foreignKeys.removeAllViews();
-				binding.foreignKeys.setVisibility(View.GONE);
-			}
-			lockOrUnlockAsNeeded();
-			setDone();
-		}
-	}
-
-	private void disableEncryptionDialog(final View view) {
-		final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
-		builder.setTitle(R.string.disable_encryption);
-		builder.setMessage(R.string.disable_encryption_message);
-		builder.setPositiveButton(R.string.disable_now, (dialog, which) -> {
-			mConversation.setNextEncryption(Message.ENCRYPTION_NONE);
-			xmppConnectionService.updateConversation(mConversation);
-			finishOk(true);
-		});
-		builder.setNegativeButton(R.string.cancel, null);
-		builder.create().show();
-	}
-
-	private boolean anyWithoutMutualPresenceSubscription(List<Jid> contactJids) {
-		for (Jid jid : contactJids) {
-			if (!mAccount.getRoster().getContact(jid).mutualPresenceSubscription()) {
-				return true;
-			}
-		}
-		return false;
-	}
-
-	private boolean foreignActuallyHasKeys() {
-		synchronized (this.foreignKeysToTrust) {
-			for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
-				if (!entry.getValue().isEmpty()) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-
-	private boolean reloadFingerprints() {
-		List<Jid> acceptedTargets = mConversation == null ? new ArrayList<>() : mConversation.getAcceptedCryptoTargets();
-		ownKeysToTrust.clear();
-		if (this.mAccount == null) {
-			return false;
-		}
-		AxolotlService service = this.mAccount.getAxolotlService();
-		Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
-		for (final IdentityKey identityKey : ownKeysSet) {
-			final String fingerprint = CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize());
-			if (!ownKeysToTrust.containsKey(fingerprint)) {
-				ownKeysToTrust.put(fingerprint, false);
-			}
-		}
-		synchronized (this.foreignKeysToTrust) {
-			foreignKeysToTrust.clear();
-			for (Jid jid : contactJids) {
-				Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
-				if (hasNoOtherTrustedKeys(jid) && ownKeysSet.isEmpty()) {
-					foreignKeysSet.addAll(service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
-				}
-				Map<String, Boolean> foreignFingerprints = new HashMap<>();
-				for (final IdentityKey identityKey : foreignKeysSet) {
-					final String fingerprint = CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize());
-					if (!foreignFingerprints.containsKey(fingerprint)) {
-						foreignFingerprints.put(fingerprint, false);
-					}
-				}
-				if (!foreignFingerprints.isEmpty() || !acceptedTargets.contains(jid)) {
-					foreignKeysToTrust.put(jid, foreignFingerprints);
-				}
-			}
-		}
-		return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
-	}
-
-	public void onBackendConnected() {
-		Intent intent = getIntent();
-		this.mAccount = extractAccount(intent);
-		if (this.mAccount != null && intent != null) {
-			String uuid = intent.getStringExtra("conversation");
-			this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
-			if (this.mPendingFingerprintVerificationUri != null) {
-				processFingerprintVerification(this.mPendingFingerprintVerificationUri);
-				this.mPendingFingerprintVerificationUri = null;
-			} else {
-				final boolean keysToTrust = reloadFingerprints();
-				if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
-					populateView();
-					invalidateOptionsMenu();
-				} else {
-					finishOk(false);
-				}
-			}
-		}
-	}
-
-	private boolean hasNoOtherTrustedKeys() {
-		return mAccount == null || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
-	}
-
-	private boolean hasNoOtherTrustedKeys(Jid contact) {
-		return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
-	}
-
-	private boolean hasPendingKeyFetches() {
-		return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(contactJids);
-	}
-
-
-	@Override
-	public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) {
-		final boolean keysToTrust = reloadFingerprints();
-		if (report != null) {
-			lastFetchReport = report;
-			runOnUiThread(() -> {
-				if (mUseCameraHintToast != null && !keysToTrust) {
-					mUseCameraHintToast.cancel();
-				}
-				switch (report) {
-					case ERROR:
-						Toast.makeText(TrustKeysActivity.this, R.string.error_fetching_omemo_key, Toast.LENGTH_SHORT).show();
-						break;
-					case SUCCESS_TRUSTED:
-						Toast.makeText(TrustKeysActivity.this, R.string.blindly_trusted_omemo_keys, Toast.LENGTH_LONG).show();
-						break;
-					case SUCCESS_VERIFIED:
-						Toast.makeText(TrustKeysActivity.this,
-								Config.X509_VERIFICATION ? R.string.verified_omemo_key_with_certificate : R.string.all_omemo_keys_have_been_verified,
-								Toast.LENGTH_LONG).show();
-						break;
-				}
-			});
-
-		}
-		if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
-			refreshUi();
-		} else {
-			runOnUiThread(() -> finishOk(false));
-
-		}
-	}
-
-	private void finishOk(boolean disabled) {
-		Intent data = new Intent();
-		data.putExtra("choice", getIntent().getIntExtra("choice", ConversationFragment.ATTACHMENT_CHOICE_INVALID));
-		data.putExtra("disabled", disabled);
-		setResult(RESULT_OK, data);
-		finish();
-	}
-
-	private void commitTrusts() {
-		for (final String fingerprint : ownKeysToTrust.keySet()) {
-			mAccount.getAxolotlService().setFingerprintTrust(
-					fingerprint,
-					FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
-		}
-		List<Jid> acceptedTargets = mConversation == null ? new ArrayList<>() : mConversation.getAcceptedCryptoTargets();
-		synchronized (this.foreignKeysToTrust) {
-			for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
-				Jid jid = entry.getKey();
-				Map<String, Boolean> value = entry.getValue();
-				if (!acceptedTargets.contains(jid)) {
-					acceptedTargets.add(jid);
-				}
-				for (final String fingerprint : value.keySet()) {
-					mAccount.getAxolotlService().setFingerprintTrust(
-							fingerprint,
-							FingerprintStatus.createActive(value.get(fingerprint)));
-				}
-			}
-		}
-		if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) {
-			mConversation.setAcceptedCryptoTargets(acceptedTargets);
-			xmppConnectionService.updateConversation(mConversation);
-		}
-	}
-
-	private void unlock() {
-		binding.saveButton.setEnabled(true);
-	}
-
-	private void lock() {
-		binding.saveButton.setEnabled(false);
-	}
-
-	private void lockOrUnlockAsNeeded() {
-		synchronized (this.foreignKeysToTrust) {
-			for (Jid jid : contactJids) {
-				Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid);
-				if (hasNoOtherTrustedKeys(jid) && (fingerprints == null || !fingerprints.containsValue(true))) {
-					lock();
-					return;
-				}
-			}
-		}
-		unlock();
-
-	}
-
-	private void setDone() {
-		binding.saveButton.setText(getString(R.string.done));
-	}
-
-	private void setFetching() {
-		binding.saveButton.setText(getString(R.string.fetching_keys));
-	}
+    private final Map<String, Boolean> ownKeysToTrust = new HashMap<>();
+    private final Map<Jid, Map<String, Boolean>> foreignKeysToTrust = new HashMap<>();
+    private final OnClickListener mCancelButtonListener =
+            v -> {
+                setResult(RESULT_CANCELED);
+                finish();
+            };
+    private List<Jid> contactJids;
+    private Account mAccount;
+    private Conversation mConversation;
+    private final OnClickListener mSaveButtonListener =
+            v -> {
+                commitTrusts();
+                finishOk(false);
+            };
+    private final AtomicBoolean mUseCameraHintShown = new AtomicBoolean(false);
+    private AxolotlService.FetchStatus lastFetchReport = AxolotlService.FetchStatus.SUCCESS;
+    private Toast mUseCameraHintToast = null;
+    private ActivityTrustKeysBinding binding;
+
+    @Override
+    protected void refreshUiReal() {
+        invalidateOptionsMenu();
+        populateView();
+    }
+
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        this.binding = DataBindingUtil.setContentView(this, R.layout.activity_trust_keys);
+        Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
+        this.contactJids = new ArrayList<>();
+        final var intent = getIntent();
+        final String[] contacts = intent == null ? null : intent.getStringArrayExtra("contacts");
+        for (final String jid : (contacts == null ? new String[0] : contacts)) {
+            try {
+                this.contactJids.add(Jid.of(jid));
+            } catch (final IllegalArgumentException ignored) {
+            }
+        }
+
+        binding.cancelButton.setOnClickListener(mCancelButtonListener);
+        binding.saveButton.setOnClickListener(mSaveButtonListener);
+
+        setSupportActionBar(binding.toolbar);
+        configureActionBar(getSupportActionBar());
+
+        if (savedInstanceState != null) {
+            mUseCameraHintShown.set(savedInstanceState.getBoolean("camera_hint_shown", false));
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle savedInstanceState) {
+        savedInstanceState.putBoolean("camera_hint_shown", mUseCameraHintShown.get());
+        super.onSaveInstanceState(savedInstanceState);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.trust_keys, menu);
+        MenuItem scanQrCode = menu.findItem(R.id.action_scan_qr_code);
+        scanQrCode.setVisible(
+                (!ownKeysToTrust.isEmpty() || foreignActuallyHasKeys())
+                        && isCameraFeatureAvailable());
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    private void showCameraToast() {
+        mUseCameraHintToast =
+                Toast.makeText(this, R.string.use_camera_icon_to_scan_barcode, Toast.LENGTH_LONG);
+        ActionBar actionBar = getSupportActionBar();
+        mUseCameraHintToast.setGravity(
+                Gravity.TOP | Gravity.END, 0, actionBar == null ? 0 : actionBar.getHeight());
+        mUseCameraHintToast.show();
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_scan_qr_code:
+                if (hasPendingKeyFetches()) {
+                    Toast.makeText(
+                                    this,
+                                    R.string.please_wait_for_keys_to_be_fetched,
+                                    Toast.LENGTH_SHORT)
+                            .show();
+                } else {
+                    ScanActivity.scan(this);
+                    // new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE"));
+                    return true;
+                }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        if (mUseCameraHintToast != null) {
+            mUseCameraHintToast.cancel();
+        }
+    }
+
+    @Override
+    protected void processFingerprintVerification(XmppUri uri) {
+        if (mConversation != null
+                && mAccount != null
+                && uri.hasFingerprints()
+                && mAccount.getAxolotlService()
+                        .getCryptoTargets(mConversation)
+                        .contains(uri.getJid())) {
+            boolean performedVerification =
+                    xmppConnectionService.verifyFingerprints(
+                            mAccount.getRoster().getContact(uri.getJid()), uri.getFingerprints());
+            boolean keys = reloadFingerprints();
+            if (performedVerification
+                    && !keys
+                    && !hasNoOtherTrustedKeys()
+                    && !hasPendingKeyFetches()) {
+                Toast.makeText(this, R.string.all_omemo_keys_have_been_verified, Toast.LENGTH_SHORT)
+                        .show();
+                finishOk(false);
+                return;
+            } else if (performedVerification) {
+                Toast.makeText(this, R.string.verified_fingerprints, Toast.LENGTH_SHORT).show();
+            }
+        } else {
+            reloadFingerprints();
+            Log.d(
+                    Config.LOGTAG,
+                    "xmpp uri was: "
+                            + uri.getJid()
+                            + " has Fingerprints: "
+                            + uri.hasFingerprints());
+            Toast.makeText(
+                            this,
+                            R.string.barcode_does_not_contain_fingerprints_for_this_chat,
+                            Toast.LENGTH_SHORT)
+                    .show();
+        }
+        populateView();
+    }
+
+    private void populateView() {
+        setTitle(getString(R.string.trust_omemo_fingerprints));
+        binding.ownKeysDetails.removeAllViews();
+        binding.foreignKeys.removeAllViews();
+        boolean hasOwnKeys = false;
+        boolean hasForeignKeys = false;
+        for (final String fingerprint : ownKeysToTrust.keySet()) {
+            hasOwnKeys = true;
+            addFingerprintRowWithListeners(
+                    binding.ownKeysDetails,
+                    mAccount,
+                    fingerprint,
+                    false,
+                    FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)),
+                    false,
+                    false,
+                    (buttonView, isChecked) -> {
+                        ownKeysToTrust.put(fingerprint, isChecked);
+                        // own fingerprints have no impact on locked status.
+                    });
+        }
+
+        synchronized (this.foreignKeysToTrust) {
+            for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+                hasForeignKeys = true;
+                KeysCardBinding keysCardBinding =
+                        DataBindingUtil.inflate(
+                                getLayoutInflater(),
+                                R.layout.keys_card,
+                                binding.foreignKeys,
+                                false);
+                final Jid jid = entry.getKey();
+                keysCardBinding.foreignKeysTitle.setText(IrregularUnicodeDetector.style(this, jid));
+                keysCardBinding.foreignKeysTitle.setOnClickListener(
+                        v -> switchToContactDetails(mAccount.getRoster().getContact(jid)));
+                final Map<String, Boolean> fingerprints = entry.getValue();
+                for (final String fingerprint : fingerprints.keySet()) {
+                    addFingerprintRowWithListeners(
+                            keysCardBinding.foreignKeysDetails,
+                            mAccount,
+                            fingerprint,
+                            false,
+                            FingerprintStatus.createActive(fingerprints.get(fingerprint)),
+                            false,
+                            false,
+                            (buttonView, isChecked) -> {
+                                fingerprints.put(fingerprint, isChecked);
+                                lockOrUnlockAsNeeded();
+                            });
+                }
+                if (fingerprints.isEmpty()) {
+                    keysCardBinding.noKeysToAccept.setVisibility(View.VISIBLE);
+                    if (hasNoOtherTrustedKeys(jid)) {
+                        if (!mAccount.getRoster().getContact(jid).mutualPresenceSubscription()) {
+                            keysCardBinding.noKeysToAccept.setText(
+                                    R.string.error_no_keys_to_trust_presence);
+                        } else {
+                            keysCardBinding.noKeysToAccept.setText(
+                                    R.string.error_no_keys_to_trust_server_error);
+                        }
+                    } else {
+                        keysCardBinding.noKeysToAccept.setText(
+                                getString(
+                                        R.string.no_keys_just_confirm,
+                                        mAccount.getRoster().getContact(jid).getDisplayName()));
+                    }
+                } else {
+                    keysCardBinding.noKeysToAccept.setVisibility(View.GONE);
+                }
+                binding.foreignKeys.addView(keysCardBinding.foreignKeysCard);
+            }
+        }
+
+        if ((hasOwnKeys || foreignActuallyHasKeys())
+                && isCameraFeatureAvailable()
+                && mUseCameraHintShown.compareAndSet(false, true)) {
+            showCameraToast();
+        }
+
+        binding.ownKeysTitle.setText(mAccount.getJid().asBareJid().toString());
+        binding.ownKeysCard.setVisibility(hasOwnKeys ? View.VISIBLE : View.GONE);
+        binding.foreignKeys.setVisibility(hasForeignKeys ? View.VISIBLE : View.GONE);
+        if (hasPendingKeyFetches()) {
+            setFetching();
+            lock();
+        } else {
+            if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
+                binding.keyErrorMessageCard.setVisibility(View.VISIBLE);
+                boolean lastReportWasError = lastFetchReport == AxolotlService.FetchStatus.ERROR;
+                boolean errorFetchingBundle =
+                        mAccount.getAxolotlService().fetchMapHasErrors(contactJids);
+                boolean errorFetchingDeviceList =
+                        mAccount.getAxolotlService().hasErrorFetchingDeviceList(contactJids);
+                boolean anyWithoutMutualPresenceSubscription =
+                        anyWithoutMutualPresenceSubscription(contactJids);
+                if (errorFetchingDeviceList) {
+                    binding.keyErrorMessage.setVisibility(View.VISIBLE);
+                    binding.keyErrorMessage.setText(R.string.error_trustkey_device_list);
+                } else if (errorFetchingBundle || lastReportWasError) {
+                    binding.keyErrorMessage.setVisibility(View.VISIBLE);
+                    binding.keyErrorMessage.setText(R.string.error_trustkey_bundle);
+                } else {
+                    binding.keyErrorMessage.setVisibility(View.GONE);
+                }
+                this.binding.keyErrorHintMutual.setVisibility(
+                        anyWithoutMutualPresenceSubscription ? View.VISIBLE : View.GONE);
+                Contact contact = mAccount.getRoster().getContact(contactJids.get(0));
+                binding.keyErrorGeneral.setText(
+                        getString(
+                                R.string.error_trustkey_general,
+                                getString(R.string.app_name),
+                                contact.getDisplayName()));
+                binding.ownKeysDetails.removeAllViews();
+                if (OmemoSetting.isAlways()) {
+                    binding.disableButton.setVisibility(View.GONE);
+                } else {
+                    binding.disableButton.setVisibility(View.VISIBLE);
+                    binding.disableButton.setOnClickListener(this::disableEncryptionDialog);
+                }
+                binding.ownKeysCard.setVisibility(View.GONE);
+                binding.foreignKeys.removeAllViews();
+                binding.foreignKeys.setVisibility(View.GONE);
+            }
+            lockOrUnlockAsNeeded();
+            setDone();
+        }
+    }
+
+    private void disableEncryptionDialog(final View view) {
+        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
+        builder.setTitle(R.string.disable_encryption);
+        builder.setMessage(R.string.disable_encryption_message);
+        builder.setPositiveButton(
+                R.string.disable_now,
+                (dialog, which) -> {
+                    mConversation.setNextEncryption(Message.ENCRYPTION_NONE);
+                    xmppConnectionService.updateConversation(mConversation);
+                    finishOk(true);
+                });
+        builder.setNegativeButton(R.string.cancel, null);
+        builder.create().show();
+    }
+
+    private boolean anyWithoutMutualPresenceSubscription(List<Jid> contactJids) {
+        for (Jid jid : contactJids) {
+            if (!mAccount.getRoster().getContact(jid).mutualPresenceSubscription()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean foreignActuallyHasKeys() {
+        synchronized (this.foreignKeysToTrust) {
+            for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+                if (!entry.getValue().isEmpty()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean reloadFingerprints() {
+        List<Jid> acceptedTargets =
+                mConversation == null
+                        ? new ArrayList<>()
+                        : mConversation.getAcceptedCryptoTargets();
+        ownKeysToTrust.clear();
+        if (this.mAccount == null) {
+            return false;
+        }
+        AxolotlService service = this.mAccount.getAxolotlService();
+        Set<IdentityKey> ownKeysSet =
+                service.getKeysWithTrust(FingerprintStatus.createActiveUndecided());
+        for (final IdentityKey identityKey : ownKeysSet) {
+            final String fingerprint =
+                    CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize());
+            if (!ownKeysToTrust.containsKey(fingerprint)) {
+                ownKeysToTrust.put(fingerprint, false);
+            }
+        }
+        synchronized (this.foreignKeysToTrust) {
+            foreignKeysToTrust.clear();
+            for (Jid jid : contactJids) {
+                Set<IdentityKey> foreignKeysSet =
+                        service.getKeysWithTrust(FingerprintStatus.createActiveUndecided(), jid);
+                if (hasNoOtherTrustedKeys(jid) && ownKeysSet.isEmpty()) {
+                    foreignKeysSet.addAll(
+                            service.getKeysWithTrust(FingerprintStatus.createActive(false), jid));
+                }
+                Map<String, Boolean> foreignFingerprints = new HashMap<>();
+                for (final IdentityKey identityKey : foreignKeysSet) {
+                    final String fingerprint =
+                            CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize());
+                    if (!foreignFingerprints.containsKey(fingerprint)) {
+                        foreignFingerprints.put(fingerprint, false);
+                    }
+                }
+                if (!foreignFingerprints.isEmpty() || !acceptedTargets.contains(jid)) {
+                    foreignKeysToTrust.put(jid, foreignFingerprints);
+                }
+            }
+        }
+        return ownKeysSet.size() + foreignKeysToTrust.size() > 0;
+    }
+
+    public void onBackendConnected() {
+        Intent intent = getIntent();
+        this.mAccount = extractAccount(intent);
+        if (this.mAccount != null && intent != null) {
+            String uuid = intent.getStringExtra("conversation");
+            this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
+            if (this.mPendingFingerprintVerificationUri != null) {
+                processFingerprintVerification(this.mPendingFingerprintVerificationUri);
+                this.mPendingFingerprintVerificationUri = null;
+            } else {
+                final boolean keysToTrust = reloadFingerprints();
+                if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
+                    populateView();
+                    invalidateOptionsMenu();
+                } else {
+                    finishOk(false);
+                }
+            }
+        }
+    }
+
+    private boolean hasNoOtherTrustedKeys() {
+        return mAccount == null
+                || mAccount.getAxolotlService().anyTargetHasNoTrustedKeys(contactJids);
+    }
+
+    private boolean hasNoOtherTrustedKeys(Jid contact) {
+        return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
+    }
+
+    private boolean hasPendingKeyFetches() {
+        return mAccount != null && mAccount.getAxolotlService().hasPendingKeyFetches(contactJids);
+    }
+
+    @Override
+    public void onKeyStatusUpdated(final AxolotlService.FetchStatus report) {
+        final boolean keysToTrust = reloadFingerprints();
+        if (report != null) {
+            lastFetchReport = report;
+            runOnUiThread(
+                    () -> {
+                        if (mUseCameraHintToast != null && !keysToTrust) {
+                            mUseCameraHintToast.cancel();
+                        }
+                        switch (report) {
+                            case ERROR:
+                                Toast.makeText(
+                                                TrustKeysActivity.this,
+                                                R.string.error_fetching_omemo_key,
+                                                Toast.LENGTH_SHORT)
+                                        .show();
+                                break;
+                            case SUCCESS_TRUSTED:
+                                Toast.makeText(
+                                                TrustKeysActivity.this,
+                                                R.string.blindly_trusted_omemo_keys,
+                                                Toast.LENGTH_LONG)
+                                        .show();
+                                break;
+                            case SUCCESS_VERIFIED:
+                                Toast.makeText(
+                                                TrustKeysActivity.this,
+                                                Config.X509_VERIFICATION
+                                                        ? R.string
+                                                                .verified_omemo_key_with_certificate
+                                                        : R.string
+                                                                .all_omemo_keys_have_been_verified,
+                                                Toast.LENGTH_LONG)
+                                        .show();
+                                break;
+                        }
+                    });
+        }
+        if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
+            refreshUi();
+        } else {
+            runOnUiThread(() -> finishOk(false));
+        }
+    }
+
+    private void finishOk(boolean disabled) {
+        Intent data = new Intent();
+        data.putExtra(
+                "choice",
+                getIntent().getIntExtra("choice", ConversationFragment.ATTACHMENT_CHOICE_INVALID));
+        data.putExtra("disabled", disabled);
+        setResult(RESULT_OK, data);
+        finish();
+    }
+
+    private void commitTrusts() {
+        for (final String fingerprint : ownKeysToTrust.keySet()) {
+            mAccount.getAxolotlService()
+                    .setFingerprintTrust(
+                            fingerprint,
+                            FingerprintStatus.createActive(ownKeysToTrust.get(fingerprint)));
+        }
+        List<Jid> acceptedTargets =
+                mConversation == null
+                        ? new ArrayList<>()
+                        : mConversation.getAcceptedCryptoTargets();
+        synchronized (this.foreignKeysToTrust) {
+            for (Map.Entry<Jid, Map<String, Boolean>> entry : foreignKeysToTrust.entrySet()) {
+                Jid jid = entry.getKey();
+                Map<String, Boolean> value = entry.getValue();
+                if (!acceptedTargets.contains(jid)) {
+                    acceptedTargets.add(jid);
+                }
+                for (final String fingerprint : value.keySet()) {
+                    mAccount.getAxolotlService()
+                            .setFingerprintTrust(
+                                    fingerprint,
+                                    FingerprintStatus.createActive(value.get(fingerprint)));
+                }
+            }
+        }
+        if (mConversation != null && mConversation.getMode() == Conversation.MODE_MULTI) {
+            mConversation.setAcceptedCryptoTargets(acceptedTargets);
+            xmppConnectionService.updateConversation(mConversation);
+        }
+    }
+
+    private void unlock() {
+        binding.saveButton.setEnabled(true);
+    }
+
+    private void lock() {
+        binding.saveButton.setEnabled(false);
+    }
+
+    private void lockOrUnlockAsNeeded() {
+        synchronized (this.foreignKeysToTrust) {
+            for (Jid jid : contactJids) {
+                Map<String, Boolean> fingerprints = foreignKeysToTrust.get(jid);
+                if (hasNoOtherTrustedKeys(jid)
+                        && (fingerprints == null || !fingerprints.containsValue(true))) {
+                    lock();
+                    return;
+                }
+            }
+        }
+        unlock();
+    }
+
+    private void setDone() {
+        binding.saveButton.setText(getString(R.string.done));
+    }
+
+    private void setFetching() {
+        binding.saveButton.setText(getString(R.string.fetching_keys));
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
index 55bf8b3ad..d4c63412b 100644
--- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
@@ -9,14 +9,11 @@ import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
 import androidx.core.content.ContextCompat;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.common.base.Strings;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ActivityUriHandlerBinding;
@@ -27,18 +24,16 @@ import eu.siacs.conversations.utils.ProvisioningUtils;
 import eu.siacs.conversations.utils.SignupUtils;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
-
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import okhttp3.Call;
 import okhttp3.Callback;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 import okhttp3.Response;
 
-import java.io.IOException;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 public class UriHandlerActivity extends BaseActivity {
 
     public static final String ACTION_SCAN_QR_CODE = "scan_qr_code";
@@ -56,7 +51,8 @@ public class UriHandlerActivity extends BaseActivity {
     }
 
     public static void scan(final Activity activity, final boolean provisioning) {
-        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
+        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
+                == PackageManager.PERMISSION_GRANTED) {
             final Intent intent = new Intent(activity, UriHandlerActivity.class);
             intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE);
             if (provisioning) {
@@ -128,7 +124,7 @@ public class UriHandlerActivity extends BaseActivity {
             final String preAuth = xmppUri.getParameter(XmppUri.PARAMETER_PRE_AUTH);
             final Jid jid = xmppUri.getJid();
             if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
-                if (jid.getEscapedLocal() != null && accounts.contains(jid.asBareJid())) {
+                if (jid.getLocal() != null && accounts.contains(jid.asBareJid())) {
                     showError(R.string.account_already_exists);
                     return false;
                 }
@@ -172,13 +168,13 @@ public class UriHandlerActivity extends BaseActivity {
                 final Class<?> clazz = findShareViaAccountClass();
                 if (clazz != null) {
                     intent = new Intent(this, clazz);
-                    intent.putExtra("contact", jid.toEscapedString());
+                    intent.putExtra("contact", jid.toString());
                     intent.putExtra("body", body);
                 } else {
                     intent = new Intent(this, StartConversationActivity.class);
                     intent.setAction(Intent.ACTION_VIEW);
                     intent.setData(uri);
-                    intent.putExtra("account", accounts.get(0).toEscapedString());
+                    intent.putExtra("account", accounts.get(0).toString());
                 }
             } else {
                 intent = new Intent(this, ShareWithActivity.class);
@@ -209,8 +205,8 @@ public class UriHandlerActivity extends BaseActivity {
     private void checkForLinkHeader(final HttpUrl url) {
         Log.d(Config.LOGTAG, "checking for link header on " + url);
         this.call =
-                HttpConnectionManager.okHttpClient(this).newCall(
-                        new Request.Builder().url(url).head().build());
+                HttpConnectionManager.okHttpClient(this)
+                        .newCall(new Request.Builder().url(url).head().build());
         this.call.enqueue(
                 new Callback() {
                     @Override
@@ -252,7 +248,7 @@ public class UriHandlerActivity extends BaseActivity {
     }
 
     private void showErrorOnUiThread(@StringRes int error) {
-        runOnUiThread(()-> showError(error));
+        runOnUiThread(() -> showError(error));
     }
 
     private static Class<?> findShareViaAccountClass() {
@@ -350,4 +346,4 @@ public class UriHandlerActivity extends BaseActivity {
         final String trimmed = Strings.nullToEmpty(input).trim();
         return trimmed.charAt(0) == '{' && trimmed.charAt(trimmed.length() - 1) == '}';
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index fa304e810..8a3fc8a54 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -733,8 +733,8 @@ public abstract class XmppActivity extends ActionBarActivity {
     public void switchToContactDetails(Contact contact, String messageFingerprint) {
         Intent intent = new Intent(this, ContactDetailsActivity.class);
         intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
-        intent.putExtra(EXTRA_ACCOUNT, contact.getAccount().getJid().asBareJid().toEscapedString());
-        intent.putExtra("contact", contact.getJid().toEscapedString());
+        intent.putExtra(EXTRA_ACCOUNT, contact.getAccount().getJid().asBareJid().toString());
+        intent.putExtra("contact", contact.getJid().toString());
         intent.putExtra("fingerprint", messageFingerprint);
         startActivity(intent);
     }
@@ -749,7 +749,7 @@ public abstract class XmppActivity extends ActionBarActivity {
 
     public void switchToAccount(Account account, boolean init, String fingerprint) {
         Intent intent = new Intent(this, EditAccountActivity.class);
-        intent.putExtra("jid", account.getJid().asBareJid().toEscapedString());
+        intent.putExtra("jid", account.getJid().asBareJid().toString());
         intent.putExtra("init", init);
         if (init) {
             intent.setFlags(
@@ -1141,7 +1141,7 @@ public abstract class XmppActivity extends ActionBarActivity {
     protected Account extractAccount(Intent intent) {
         final String jid = intent != null ? intent.getStringExtra(EXTRA_ACCOUNT) : null;
         try {
-            return jid != null ? xmppConnectionService.findAccountByJid(Jid.ofEscaped(jid)) : null;
+            return jid != null ? xmppConnectionService.findAccountByJid(Jid.of(jid)) : null;
         } catch (IllegalArgumentException e) {
             return null;
         }
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
index 88475c580..b52280181 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
@@ -4,19 +4,14 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
-
 import androidx.annotation.NonNull;
 import androidx.databinding.DataBindingUtil;
-
 import com.google.android.material.color.MaterialColors;
-
-import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.ItemAccountBinding;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.ui.XmppActivity;
 import eu.siacs.conversations.ui.util.AvatarWorkerTask;
-
 import java.util.List;
 
 public class AccountAdapter extends ArrayAdapter<Account> {
@@ -42,27 +37,42 @@ public class AccountAdapter extends ArrayAdapter<Account> {
         final Account account = getItem(position);
         final ViewHolder viewHolder;
         if (view == null) {
-            ItemAccountBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_account, parent, false);
+            ItemAccountBinding binding =
+                    DataBindingUtil.inflate(
+                            LayoutInflater.from(parent.getContext()),
+                            R.layout.item_account,
+                            parent,
+                            false);
             view = binding.getRoot();
             viewHolder = new ViewHolder(binding);
             view.setTag(viewHolder);
         } else {
             viewHolder = (ViewHolder) view.getTag();
         }
-        viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toEscapedString());
+        viewHolder.binding.accountJid.setText(account.getJid().asBareJid().toString());
         AvatarWorkerTask.loadAvatar(account, viewHolder.binding.accountImage, R.dimen.avatar);
-        viewHolder.binding.accountStatus.setText(getContext().getString(account.getStatus().getReadableId()));
+        viewHolder.binding.accountStatus.setText(
+                getContext().getString(account.getStatus().getReadableId()));
         switch (account.getStatus()) {
             case ONLINE:
-                viewHolder.binding.accountStatus.setTextColor(MaterialColors.getColor(viewHolder.binding.accountStatus, com.google.android.material.R.attr.colorPrimary));
+                viewHolder.binding.accountStatus.setTextColor(
+                        MaterialColors.getColor(
+                                viewHolder.binding.accountStatus,
+                                com.google.android.material.R.attr.colorPrimary));
                 break;
             case DISABLED:
             case LOGGED_OUT:
             case CONNECTING:
-                viewHolder.binding.accountStatus.setTextColor(MaterialColors.getColor(viewHolder.binding.accountStatus, com.google.android.material.R.attr.colorOnSurfaceVariant));
+                viewHolder.binding.accountStatus.setTextColor(
+                        MaterialColors.getColor(
+                                viewHolder.binding.accountStatus,
+                                com.google.android.material.R.attr.colorOnSurfaceVariant));
                 break;
             default:
-                viewHolder.binding.accountStatus.setTextColor(MaterialColors.getColor(viewHolder.binding.accountStatus, com.google.android.material.R.attr.colorError));
+                viewHolder.binding.accountStatus.setTextColor(
+                        MaterialColors.getColor(
+                                viewHolder.binding.accountStatus,
+                                com.google.android.material.R.attr.colorError));
                 break;
         }
         final boolean isDisabled = (account.getStatus() == Account.State.DISABLED);
@@ -73,11 +83,12 @@ public class AccountAdapter extends ArrayAdapter<Account> {
         } else {
             viewHolder.binding.tglAccountStatus.setVisibility(View.GONE);
         }
-        viewHolder.binding.tglAccountStatus.setOnCheckedChangeListener((compoundButton, b) -> {
-            if (b == isDisabled && activity instanceof OnTglAccountState) {
-                ((OnTglAccountState) activity).onClickTglAccountState(account, b);
-            }
-        });
+        viewHolder.binding.tglAccountStatus.setOnCheckedChangeListener(
+                (compoundButton, b) -> {
+                    if (b == isDisabled && activity instanceof OnTglAccountState) {
+                        ((OnTglAccountState) activity).onClickTglAccountState(account, b);
+                    }
+                });
         return view;
     }
 
@@ -89,10 +100,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
         }
     }
 
-
-
     public interface OnTglAccountState {
         void onClickTglAccountState(Account account, boolean state);
     }
-
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
index b085cb964..88f7636bf 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java
@@ -3,14 +3,10 @@ package eu.siacs.conversations.ui.adapter;
 import android.content.Context;
 import android.widget.ArrayAdapter;
 import android.widget.Filter;
-
 import androidx.annotation.NonNull;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Ordering;
-
 import eu.siacs.conversations.Config;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -22,61 +18,66 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
     private static final Pattern E164_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$");
 
     private List<String> domains;
-    private final Filter domainFilter = new Filter() {
+    private final Filter domainFilter =
+            new Filter() {
 
-        @Override
-        protected FilterResults performFiltering(final CharSequence constraint) {
-            final ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
-            final String[] split = constraint == null ? new String[0] : constraint.toString().split("@");
-            if (split.length == 1) {
-                final String local = split[0].toLowerCase(Locale.ENGLISH);
-                if (Config.QUICKSY_DOMAIN != null && E164_PATTERN.matcher(local).matches()) {
-                    builder.add(local + '@' + Config.QUICKSY_DOMAIN.toEscapedString());
-                } else {
-                    for (String domain : domains) {
-                        builder.add(local + '@' + domain);
-                    }
-                }
-            } else if (split.length == 2) {
-                final String localPart = split[0].toLowerCase(Locale.ENGLISH);
-                final String domainPart = split[1].toLowerCase(Locale.ENGLISH);
-                if (domains.contains(domainPart)) {
-                    return new FilterResults();
-                }
-                for (final String domain : domains) {
-                    if (domain.contains(domainPart)) {
-                        builder.add(localPart + "@" + domain);
+                @Override
+                protected FilterResults performFiltering(final CharSequence constraint) {
+                    final ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
+                    final String[] split =
+                            constraint == null ? new String[0] : constraint.toString().split("@");
+                    if (split.length == 1) {
+                        final String local = split[0].toLowerCase(Locale.ENGLISH);
+                        if (Config.QUICKSY_DOMAIN != null
+                                && E164_PATTERN.matcher(local).matches()) {
+                            builder.add(local + '@' + Config.QUICKSY_DOMAIN.toString());
+                        } else {
+                            for (String domain : domains) {
+                                builder.add(local + '@' + domain);
+                            }
+                        }
+                    } else if (split.length == 2) {
+                        final String localPart = split[0].toLowerCase(Locale.ENGLISH);
+                        final String domainPart = split[1].toLowerCase(Locale.ENGLISH);
+                        if (domains.contains(domainPart)) {
+                            return new FilterResults();
+                        }
+                        for (final String domain : domains) {
+                            if (domain.contains(domainPart)) {
+                                builder.add(localPart + "@" + domain);
+                            }
+                        }
+                    } else {
+                        return new FilterResults();
                     }
+                    final var suggestions = builder.build();
+                    final FilterResults filterResults = new FilterResults();
+                    filterResults.values = suggestions;
+                    filterResults.count = suggestions.size();
+                    return filterResults;
                 }
-            } else {
-                return new FilterResults();
-            }
-            final var suggestions = builder.build();
-            final FilterResults filterResults = new FilterResults();
-            filterResults.values = suggestions;
-            filterResults.count = suggestions.size();
-            return filterResults;
-        }
 
-        @Override
-        protected void publishResults(final CharSequence constraint, final FilterResults results) {
-            final ImmutableList.Builder<String> suggestions = new ImmutableList.Builder<>();
-            if (results.values instanceof Collection<?> collection) {
-                for(final Object item : collection) {
-                    if (item instanceof String string) {
-                        suggestions.add(string);
+                @Override
+                protected void publishResults(
+                        final CharSequence constraint, final FilterResults results) {
+                    final ImmutableList.Builder<String> suggestions = new ImmutableList.Builder<>();
+                    if (results.values instanceof Collection<?> collection) {
+                        for (final Object item : collection) {
+                            if (item instanceof String string) {
+                                suggestions.add(string);
+                            }
+                        }
                     }
+                    clear();
+                    addAll(suggestions.build());
+                    notifyDataSetChanged();
                 }
-            }
-            clear();
-            addAll(suggestions.build());
-            notifyDataSetChanged();
-        }
-    };
+            };
 
-    public KnownHostsAdapter(final Context context, final int viewResourceId, final Collection<String> knownHosts) {
+    public KnownHostsAdapter(
+            final Context context, final int viewResourceId, final Collection<String> knownHosts) {
         super(context, viewResourceId, new ArrayList<>());
-        domains =  Ordering.natural().sortedCopy(knownHosts);
+        domains = Ordering.natural().sortedCopy(knownHosts);
     }
 
     public KnownHostsAdapter(final Context context, final int viewResourceId) {
diff --git a/src/main/java/eu/siacs/conversations/ui/fragment/settings/UpSettingsFragment.java b/src/main/java/eu/siacs/conversations/ui/fragment/settings/UpSettingsFragment.java
index 771acbbe1..2b279c905 100644
--- a/src/main/java/eu/siacs/conversations/ui/fragment/settings/UpSettingsFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/fragment/settings/UpSettingsFragment.java
@@ -2,20 +2,16 @@ package eu.siacs.conversations.ui.fragment.settings;
 
 import android.os.Bundle;
 import android.widget.Toast;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.preference.EditTextPreference;
 import androidx.preference.ListPreference;
-
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.receiver.UnifiedPushDistributor;
 import eu.siacs.conversations.xmpp.Jid;
-
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Arrays;
@@ -31,29 +27,38 @@ public class UpSettingsFragment extends XmppPreferenceFragment {
     @Override
     public void onBackendConnected() {
         final ListPreference upAccounts = findPreference(UnifiedPushDistributor.PREFERENCE_ACCOUNT);
-        final EditTextPreference pushServer = findPreference(UnifiedPushDistributor.PREFERENCE_PUSH_SERVER);
+        final EditTextPreference pushServer =
+                findPreference(UnifiedPushDistributor.PREFERENCE_PUSH_SERVER);
         if (upAccounts == null || pushServer == null) {
             throw new IllegalStateException();
         }
-        pushServer.setOnPreferenceChangeListener((preference, newValue) -> {
-            if (newValue instanceof String string) {
-                if (Strings.isNullOrEmpty(string) || isJidInvalid(string) || isHttpUri(string)) {
-                    Toast.makeText(requireActivity(),R.string.invalid_jid,Toast.LENGTH_LONG).show();
-                    return false;
-                } else {
-                    return true;
-                }
-            } else {
-                Toast.makeText(requireActivity(),R.string.invalid_jid,Toast.LENGTH_LONG).show();
-                return false;
-            }
-        });
+        pushServer.setOnPreferenceChangeListener(
+                (preference, newValue) -> {
+                    if (newValue instanceof String string) {
+                        if (Strings.isNullOrEmpty(string)
+                                || isJidInvalid(string)
+                                || isHttpUri(string)) {
+                            Toast.makeText(
+                                            requireActivity(),
+                                            R.string.invalid_jid,
+                                            Toast.LENGTH_LONG)
+                                    .show();
+                            return false;
+                        } else {
+                            return true;
+                        }
+                    } else {
+                        Toast.makeText(requireActivity(), R.string.invalid_jid, Toast.LENGTH_LONG)
+                                .show();
+                        return false;
+                    }
+                });
         reconfigureUpAccountPreference(upAccounts);
     }
 
     private static boolean isJidInvalid(final String input) {
         try {
-            final var jid = Jid.ofEscaped(input);
+            final var jid = Jid.ofUserInput(input);
             return !jid.isBareJid();
         } catch (final IllegalArgumentException e) {
             return true;
@@ -67,16 +72,15 @@ public class UpSettingsFragment extends XmppPreferenceFragment {
         } catch (final URISyntaxException e) {
             return false;
         }
-        return Arrays.asList("http","https").contains(uri.getScheme());
+        return Arrays.asList("http", "https").contains(uri.getScheme());
     }
 
-
     private void reconfigureUpAccountPreference(final ListPreference listPreference) {
         final List<CharSequence> accounts =
                 ImmutableList.copyOf(
                         Lists.transform(
                                 requireService().getAccounts(),
-                                a -> a.getJid().asBareJid().toEscapedString()));
+                                a -> a.getJid().asBareJid().toString()));
         final ImmutableList.Builder<CharSequence> entries = new ImmutableList.Builder<>();
         final ImmutableList.Builder<CharSequence> entryValues = new ImmutableList.Builder<>();
         entries.add(getString(R.string.no_account_deactivated));
diff --git a/src/main/java/eu/siacs/conversations/utils/AccountUtils.java b/src/main/java/eu/siacs/conversations/utils/AccountUtils.java
index 4b2c2957f..bcfffe8c1 100644
--- a/src/main/java/eu/siacs/conversations/utils/AccountUtils.java
+++ b/src/main/java/eu/siacs/conversations/utils/AccountUtils.java
@@ -5,20 +5,16 @@ import android.content.Intent;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.widget.Toast;
-
 import com.google.common.primitives.Bytes;
 import com.google.common.primitives.Longs;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.XmppActivity;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
 
 public class AccountUtils {
 
@@ -50,9 +46,7 @@ public class AccountUtils {
 
     public static UUID createUuid4(long mostSigBits, long leastSigBits) {
         final byte[] bytes =
-                Bytes.concat(
-                        Longs.toByteArray(mostSigBits),
-                        Longs.toByteArray(leastSigBits));
+                Bytes.concat(Longs.toByteArray(mostSigBits), Longs.toByteArray(leastSigBits));
         bytes[6] &= 0x0f; /* clear version        */
         bytes[6] |= 0x40; /* set to version 4     */
         bytes[8] &= 0x3f; /* clear variant        */
@@ -65,7 +59,7 @@ public class AccountUtils {
         final ArrayList<String> accounts = new ArrayList<>();
         for (final Account account : service.getAccounts()) {
             if (account.isEnabled()) {
-                accounts.add(account.getJid().asBareJid().toEscapedString());
+                accounts.add(account.getJid().asBareJid().toString());
             }
         }
         return accounts;
diff --git a/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java b/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java
index 3b536c27a..21ffeadda 100644
--- a/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java
+++ b/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java
@@ -1,13 +1,11 @@
 package eu.siacs.conversations.utils;
 
 import androidx.annotation.NonNull;
-
+import eu.siacs.conversations.xmpp.Jid;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 
-import eu.siacs.conversations.xmpp.Jid;
-
 public class BackupFileHeader {
 
     private static final int VERSION = 2;
@@ -18,17 +16,22 @@ public class BackupFileHeader {
     private final byte[] iv;
     private final byte[] salt;
 
-
     @NonNull
     @Override
     public String toString() {
-        return "BackupFileHeader{" +
-                "app='" + app + '\'' +
-                ", jid=" + jid +
-                ", timestamp=" + timestamp +
-                ", iv=" + CryptoHelper.bytesToHex(iv) +
-                ", salt=" + CryptoHelper.bytesToHex(salt) +
-                '}';
+        return "BackupFileHeader{"
+                + "app='"
+                + app
+                + '\''
+                + ", jid="
+                + jid
+                + ", timestamp="
+                + timestamp
+                + ", iv="
+                + CryptoHelper.bytesToHex(iv)
+                + ", salt="
+                + CryptoHelper.bytesToHex(salt)
+                + '}';
     }
 
     public BackupFileHeader(String app, Jid jid, long timestamp, byte[] iv, byte[] salt) {
@@ -42,7 +45,7 @@ public class BackupFileHeader {
     public void write(DataOutputStream dataOutputStream) throws IOException {
         dataOutputStream.writeInt(VERSION);
         dataOutputStream.writeUTF(app);
-        dataOutputStream.writeUTF(jid.asBareJid().toEscapedString());
+        dataOutputStream.writeUTF(jid.asBareJid().toString());
         dataOutputStream.writeLong(timestamp);
         dataOutputStream.write(iv);
         dataOutputStream.write(salt);
@@ -61,10 +64,13 @@ public class BackupFileHeader {
             throw new OutdatedBackupFileVersion();
         }
         if (version != VERSION) {
-            throw new IllegalArgumentException("Backup File version was " + version + " but app only supports version " + VERSION);
+            throw new IllegalArgumentException(
+                    "Backup File version was "
+                            + version
+                            + " but app only supports version "
+                            + VERSION);
         }
         return new BackupFileHeader(app, Jid.of(jid), timestamp, iv, salt);
-
     }
 
     public byte[] getSalt() {
@@ -87,7 +93,5 @@ public class BackupFileHeader {
         return timestamp;
     }
 
-    public static class OutdatedBackupFileVersion extends RuntimeException {
-
-    }
+    public static class OutdatedBackupFileVersion extends RuntimeException {}
 }
diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
index 34f5caccf..6d6232b0b 100644
--- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java
@@ -5,14 +5,12 @@ import static eu.siacs.conversations.utils.Random.SECURE_RANDOM;
 import android.os.Bundle;
 import android.util.Base64;
 import android.util.Pair;
-
 import androidx.annotation.StringRes;
-
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x500.style.BCStyle;
-import org.bouncycastle.asn1.x500.style.IETFUtils;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
-
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.xmpp.Jid;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -28,22 +26,22 @@ import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.regex.Pattern;
-
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.xmpp.Jid;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x500.style.IETFUtils;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 
 public final class CryptoHelper {
 
-    public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
-    final public static byte[] ONE = new byte[]{0, 0, 0, 1};
-    private static final char[] CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789+-/#$!?".toCharArray();
+    public static final Pattern UUID_PATTERN =
+            Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}");
+    public static final byte[] ONE = new byte[] {0, 0, 0, 1};
+    private static final char[] CHARS =
+            "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789+-/#$!?".toCharArray();
     private static final int PW_LENGTH = 12;
     private static final char[] VOWELS = "aeiou".toCharArray();
     private static final char[] CONSONANTS = "bcfghjklmnpqrstvwxyz".toCharArray();
-    private final static char[] hexArray = "0123456789abcdef".toCharArray();
+    private static final char[] hexArray = "0123456789abcdef".toCharArray();
 
     public static String bytesToHex(byte[] bytes) {
         char[] hexChars = new char[bytes.length * 2];
@@ -68,7 +66,10 @@ public final class CryptoHelper {
         char[] output = new char[rand * 2 + (5 - rand)];
         boolean vowel = SECURE_RANDOM.nextBoolean();
         for (int i = 0; i < output.length; ++i) {
-            output[i] = vowel ? VOWELS[SECURE_RANDOM.nextInt(VOWELS.length)] : CONSONANTS[SECURE_RANDOM.nextInt(CONSONANTS.length)];
+            output[i] =
+                    vowel
+                            ? VOWELS[SECURE_RANDOM.nextInt(VOWELS.length)]
+                            : CONSONANTS[SECURE_RANDOM.nextInt(CONSONANTS.length)];
             vowel = !vowel;
         }
         return String.valueOf(output);
@@ -78,8 +79,10 @@ public final class CryptoHelper {
         int len = hexString.length();
         byte[] array = new byte[len / 2];
         for (int i = 0; i < len; i += 2) {
-            array[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
-                    .digit(hexString.charAt(i + 1), 16));
+            array[i / 2] =
+                    (byte)
+                            ((Character.digit(hexString.charAt(i), 16) << 4)
+                                    + Character.digit(hexString.charAt(i + 1), 16));
         }
         return array;
     }
@@ -95,9 +98,7 @@ public final class CryptoHelper {
         return result;
     }
 
-    /**
-     * Escapes usernames or passwords for SASL.
-     */
+    /** Escapes usernames or passwords for SASL. */
     public static String saslEscape(final String s) {
         final StringBuilder sb = new StringBuilder((int) (s.length() * 1.1));
         for (int i = 0; i < s.length(); i++) {
@@ -149,7 +150,8 @@ public final class CryptoHelper {
     }
 
     public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) {
-        final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
+        final Collection<String> cipherSuites =
+                new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
         final List<String> platformCiphers = Arrays.asList(platformSupportedCipherSuites);
         cipherSuites.retainAll(platformCiphers);
         cipherSuites.addAll(platformCiphers);
@@ -172,7 +174,10 @@ public final class CryptoHelper {
         }
     }
 
-    public static Pair<Jid, String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, IllegalArgumentException, CertificateParsingException {
+    public static Pair<Jid, String> extractJidAndName(X509Certificate certificate)
+            throws CertificateEncodingException,
+                    IllegalArgumentException,
+                    CertificateParsingException {
         Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
         List<String> emails = new ArrayList<>();
         if (alternativeNames != null) {
@@ -185,9 +190,15 @@ public final class CryptoHelper {
         }
         X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
         if (emails.size() == 0 && x500name.getRDNs(BCStyle.EmailAddress).length > 0) {
-            emails.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
+            emails.add(
+                    IETFUtils.valueToString(
+                            x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()));
         }
-        String name = x500name.getRDNs(BCStyle.CN).length > 0 ? IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue()) : null;
+        String name =
+                x500name.getRDNs(BCStyle.CN).length > 0
+                        ? IETFUtils.valueToString(
+                                x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue())
+                        : null;
         if (emails.size() >= 1) {
             return new Pair<>(Jid.of(emails.get(0)), name);
         } else if (name != null) {
@@ -209,26 +220,33 @@ public final class CryptoHelper {
             JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate);
             X500Name subject = holder.getSubject();
             try {
-                information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+                information.putString(
+                        "subject_cn",
+                        subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
             } catch (Exception e) {
-                //ignored
+                // ignored
             }
             try {
-                information.putString("subject_o", subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+                information.putString(
+                        "subject_o",
+                        subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
             } catch (Exception e) {
-                //ignored
+                // ignored
             }
 
             X500Name issuer = holder.getIssuer();
             try {
-                information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
+                information.putString(
+                        "issuer_cn",
+                        issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString());
             } catch (Exception e) {
-                //ignored
+                // ignored
             }
             try {
-                information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
+                information.putString(
+                        "issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString());
             } catch (Exception e) {
-                //ignored
+                // ignored
             }
             try {
                 information.putString("sha1", getFingerprintCert(certificate.getEncoded()));
@@ -248,7 +266,7 @@ public final class CryptoHelper {
     }
 
     public static String getFingerprint(Jid jid, String androidId) {
-        return getFingerprint(jid.toEscapedString() + "\00" + androidId);
+        return getFingerprint(jid.toString() + "\00" + androidId);
     }
 
     public static String getAccountFingerprint(Account account, String androidId) {
@@ -268,8 +286,9 @@ public final class CryptoHelper {
         return switch (encryption) {
             case Message.ENCRYPTION_OTR -> R.string.encryption_choice_otr;
             case Message.ENCRYPTION_AXOLOTL,
-                    Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE,
-                    Message.ENCRYPTION_AXOLOTL_FAILED -> R.string.encryption_choice_omemo;
+                            Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE,
+                            Message.ENCRYPTION_AXOLOTL_FAILED ->
+                    R.string.encryption_choice_omemo;
             case Message.ENCRYPTION_PGP -> R.string.encryption_choice_pgp;
             default -> R.string.encryption_choice_unencrypted;
         };
@@ -280,6 +299,8 @@ public final class CryptoHelper {
             return false;
         }
         final String u = url.toLowerCase();
-        return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://")) && u.endsWith(".pgp");
+        return !u.contains(" ")
+                && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://"))
+                && u.endsWith(".pgp");
     }
 }
diff --git a/src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java b/src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java
index 5cc093249..9dcdbb15e 100644
--- a/src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java
+++ b/src/main/java/eu/siacs/conversations/utils/IrregularUnicodeDetector.java
@@ -37,11 +37,9 @@ import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 import android.util.LruCache;
-
 import androidx.annotation.ColorInt;
-
 import com.google.android.material.color.MaterialColors;
-
+import eu.siacs.conversations.xmpp.Jid;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -54,224 +52,234 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.xmpp.Jid;
-
 public class IrregularUnicodeDetector {
 
-	private static final Map<Character.UnicodeBlock, Character.UnicodeBlock> NORMALIZATION_MAP;
-	private static final LruCache<Jid, PatternTuple> CACHE = new LruCache<>(4096);
-	private static final List<String> AMBIGUOUS_CYRILLIC = Arrays.asList("а","г","е","ѕ","і","ј","ķ","ԛ","о","р","с","у","х");
-
-	static {
-		Map<Character.UnicodeBlock, Character.UnicodeBlock> temp = new HashMap<>();
-		temp.put(Character.UnicodeBlock.LATIN_1_SUPPLEMENT, Character.UnicodeBlock.BASIC_LATIN);
-		NORMALIZATION_MAP = Collections.unmodifiableMap(temp);
-	}
+    private static final Map<Character.UnicodeBlock, Character.UnicodeBlock> NORMALIZATION_MAP;
+    private static final LruCache<Jid, PatternTuple> CACHE = new LruCache<>(4096);
+    private static final List<String> AMBIGUOUS_CYRILLIC =
+            Arrays.asList("а", "г", "е", "ѕ", "і", "ј", "ķ", "ԛ", "о", "р", "с", "у", "х");
 
-	private static Character.UnicodeBlock normalize(Character.UnicodeBlock in) {
-		if (NORMALIZATION_MAP.containsKey(in)) {
-			return NORMALIZATION_MAP.get(in);
-		} else {
-			return in;
-		}
-	}
+    static {
+        Map<Character.UnicodeBlock, Character.UnicodeBlock> temp = new HashMap<>();
+        temp.put(Character.UnicodeBlock.LATIN_1_SUPPLEMENT, Character.UnicodeBlock.BASIC_LATIN);
+        NORMALIZATION_MAP = Collections.unmodifiableMap(temp);
+    }
 
-	public static Spannable style(final Context context, Jid jid) {
-		return style(jid, MaterialColors.getColor(context, com.google.android.material.R.attr.colorError,"colorError not found"));
-	}
+    private static Character.UnicodeBlock normalize(Character.UnicodeBlock in) {
+        if (NORMALIZATION_MAP.containsKey(in)) {
+            return NORMALIZATION_MAP.get(in);
+        } else {
+            return in;
+        }
+    }
 
-	private static Spannable style(Jid jid, @ColorInt int color) {
-		PatternTuple patternTuple = find(jid);
-		SpannableStringBuilder builder = new SpannableStringBuilder();
-		if (jid.getEscapedLocal() != null && patternTuple.local != null) {
-			SpannableString local = new SpannableString(jid.getEscapedLocal());
-			colorize(local, patternTuple.local, color);
-			builder.append(local);
-			builder.append('@');
-		}
-		if (jid.getDomain() != null) {
-			String[] labels = jid.getDomain().toEscapedString().split("\\.");
-			for (int i = 0; i < labels.length; ++i) {
-				SpannableString spannableString = new SpannableString(labels[i]);
-				colorize(spannableString, patternTuple.domain.get(i), color);
-				if (i != 0) {
-					builder.append('.');
-				}
-				builder.append(spannableString);
-			}
-		}
-		if (builder.length() != 0 && jid.getResource() != null) {
-			builder.append('/');
-			builder.append(jid.getResource());
-		}
-		return builder;
-	}
+    public static Spannable style(final Context context, Jid jid) {
+        return style(
+                jid,
+                MaterialColors.getColor(
+                        context,
+                        com.google.android.material.R.attr.colorError,
+                        "colorError not found"));
+    }
 
-	private static void colorize(SpannableString spannableString, Pattern pattern, @ColorInt int color) {
-		Matcher matcher = pattern.matcher(spannableString);
-		while (matcher.find()) {
-			if (matcher.start() < matcher.end()) {
-				spannableString.setSpan(new ForegroundColorSpan(color), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-			}
-		}
-	}
+    private static Spannable style(Jid jid, @ColorInt int color) {
+        PatternTuple patternTuple = find(jid);
+        SpannableStringBuilder builder = new SpannableStringBuilder();
+        if (jid.getLocal() != null && patternTuple.local != null) {
+            SpannableString local = new SpannableString(jid.getLocal());
+            colorize(local, patternTuple.local, color);
+            builder.append(local);
+            builder.append('@');
+        }
+        if (jid.getDomain() != null) {
+            String[] labels = jid.getDomain().toString().split("\\.");
+            for (int i = 0; i < labels.length; ++i) {
+                SpannableString spannableString = new SpannableString(labels[i]);
+                colorize(spannableString, patternTuple.domain.get(i), color);
+                if (i != 0) {
+                    builder.append('.');
+                }
+                builder.append(spannableString);
+            }
+        }
+        if (builder.length() != 0 && jid.getResource() != null) {
+            builder.append('/');
+            builder.append(jid.getResource());
+        }
+        return builder;
+    }
 
-	private static Map<Character.UnicodeBlock, List<String>> mapCompat(String word) {
-		Map<Character.UnicodeBlock, List<String>> map = new HashMap<>();
-		final int length = word.length();
-		for (int offset = 0; offset < length; ) {
-			final int codePoint = word.codePointAt(offset);
-			offset += Character.charCount(codePoint);
-			if (!Character.isLetter(codePoint)) {
-				continue;
-			}
-			Character.UnicodeBlock block = normalize(Character.UnicodeBlock.of(codePoint));
-			List<String> codePoints;
-			if (map.containsKey(block)) {
-				codePoints = map.get(block);
-			} else {
-				codePoints = new ArrayList<>();
-				map.put(block, codePoints);
-			}
-			codePoints.add(String.copyValueOf(Character.toChars(codePoint)));
-		}
-		return map;
-	}
+    private static void colorize(
+            SpannableString spannableString, Pattern pattern, @ColorInt int color) {
+        Matcher matcher = pattern.matcher(spannableString);
+        while (matcher.find()) {
+            if (matcher.start() < matcher.end()) {
+                spannableString.setSpan(
+                        new ForegroundColorSpan(color),
+                        matcher.start(),
+                        matcher.end(),
+                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            }
+        }
+    }
 
-	@TargetApi(Build.VERSION_CODES.N)
-	private static Map<Character.UnicodeScript, List<String>> map(String word) {
-		Map<Character.UnicodeScript, List<String>> map = new HashMap<>();
-		final int length = word.length();
-		for (int offset = 0; offset < length; ) {
-			final int codePoint = word.codePointAt(offset);
-			Character.UnicodeScript script = Character.UnicodeScript.of(codePoint);
-			if (script != Character.UnicodeScript.COMMON) {
-				List<String> codePoints;
-				if (map.containsKey(script)) {
-					codePoints = map.get(script);
-				} else {
-					codePoints = new ArrayList<>();
-					map.put(script, codePoints);
-				}
-				codePoints.add(String.copyValueOf(Character.toChars(codePoint)));
-			}
-			offset += Character.charCount(codePoint);
-		}
-		return map;
-	}
+    private static Map<Character.UnicodeBlock, List<String>> mapCompat(String word) {
+        Map<Character.UnicodeBlock, List<String>> map = new HashMap<>();
+        final int length = word.length();
+        for (int offset = 0; offset < length; ) {
+            final int codePoint = word.codePointAt(offset);
+            offset += Character.charCount(codePoint);
+            if (!Character.isLetter(codePoint)) {
+                continue;
+            }
+            Character.UnicodeBlock block = normalize(Character.UnicodeBlock.of(codePoint));
+            List<String> codePoints;
+            if (map.containsKey(block)) {
+                codePoints = map.get(block);
+            } else {
+                codePoints = new ArrayList<>();
+                map.put(block, codePoints);
+            }
+            codePoints.add(String.copyValueOf(Character.toChars(codePoint)));
+        }
+        return map;
+    }
 
-	private static Set<String> eliminateFirstAndGetCodePointsCompat(Map<Character.UnicodeBlock, List<String>> map) {
-		return eliminateFirstAndGetCodePoints(map, Character.UnicodeBlock.BASIC_LATIN);
-	}
+    @TargetApi(Build.VERSION_CODES.N)
+    private static Map<Character.UnicodeScript, List<String>> map(String word) {
+        Map<Character.UnicodeScript, List<String>> map = new HashMap<>();
+        final int length = word.length();
+        for (int offset = 0; offset < length; ) {
+            final int codePoint = word.codePointAt(offset);
+            Character.UnicodeScript script = Character.UnicodeScript.of(codePoint);
+            if (script != Character.UnicodeScript.COMMON) {
+                List<String> codePoints;
+                if (map.containsKey(script)) {
+                    codePoints = map.get(script);
+                } else {
+                    codePoints = new ArrayList<>();
+                    map.put(script, codePoints);
+                }
+                codePoints.add(String.copyValueOf(Character.toChars(codePoint)));
+            }
+            offset += Character.charCount(codePoint);
+        }
+        return map;
+    }
 
-	@TargetApi(Build.VERSION_CODES.N)
-	private static Set<String> eliminateFirstAndGetCodePoints(Map<Character.UnicodeScript, List<String>> map) {
-		return eliminateFirstAndGetCodePoints(map, Character.UnicodeScript.COMMON);
-	}
+    private static Set<String> eliminateFirstAndGetCodePointsCompat(
+            Map<Character.UnicodeBlock, List<String>> map) {
+        return eliminateFirstAndGetCodePoints(map, Character.UnicodeBlock.BASIC_LATIN);
+    }
 
-	private static <T> Set<String> eliminateFirstAndGetCodePoints(Map<T, List<String>> map, T defaultPick) {
-		T pick = defaultPick;
-		int size = 0;
-		for (Map.Entry<T, List<String>> entry : map.entrySet()) {
-			if (entry.getValue().size() > size) {
-				size = entry.getValue().size();
-				pick = entry.getKey();
-			}
-		}
-		map.remove(pick);
-		Set<String> all = new HashSet<>();
-		for (List<String> codePoints : map.values()) {
-			all.addAll(codePoints);
-		}
-		return all;
-	}
+    @TargetApi(Build.VERSION_CODES.N)
+    private static Set<String> eliminateFirstAndGetCodePoints(
+            Map<Character.UnicodeScript, List<String>> map) {
+        return eliminateFirstAndGetCodePoints(map, Character.UnicodeScript.COMMON);
+    }
 
-	private static Set<String> findIrregularCodePoints(String word) {
-		Set<String> codePoints;
-		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
-			final Map<Character.UnicodeBlock, List<String>> map = mapCompat(word);
-			final Set<String> set = asSet(map);
-			if (containsOnlyAmbiguousCyrillic(set)) {
-				return set;
-			}
-			codePoints = eliminateFirstAndGetCodePointsCompat(map);
-		} else {
-			final Map<Character.UnicodeScript, List<String>> map = map(word);
-			final Set<String> set = asSet(map);
-			if (containsOnlyAmbiguousCyrillic(set)) {
-				return set;
-			}
-			codePoints = eliminateFirstAndGetCodePoints(map);
-		}
-		return codePoints;
-	}
+    private static <T> Set<String> eliminateFirstAndGetCodePoints(
+            Map<T, List<String>> map, T defaultPick) {
+        T pick = defaultPick;
+        int size = 0;
+        for (Map.Entry<T, List<String>> entry : map.entrySet()) {
+            if (entry.getValue().size() > size) {
+                size = entry.getValue().size();
+                pick = entry.getKey();
+            }
+        }
+        map.remove(pick);
+        Set<String> all = new HashSet<>();
+        for (List<String> codePoints : map.values()) {
+            all.addAll(codePoints);
+        }
+        return all;
+    }
 
-	private static Set<String> asSet(Map<?, List<String>> map) {
-		final Set<String> flat = new HashSet<>();
-		for(List<String> value : map.values()) {
-			flat.addAll(value);
-		}
-		return flat;
-	}
+    private static Set<String> findIrregularCodePoints(String word) {
+        Set<String> codePoints;
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            final Map<Character.UnicodeBlock, List<String>> map = mapCompat(word);
+            final Set<String> set = asSet(map);
+            if (containsOnlyAmbiguousCyrillic(set)) {
+                return set;
+            }
+            codePoints = eliminateFirstAndGetCodePointsCompat(map);
+        } else {
+            final Map<Character.UnicodeScript, List<String>> map = map(word);
+            final Set<String> set = asSet(map);
+            if (containsOnlyAmbiguousCyrillic(set)) {
+                return set;
+            }
+            codePoints = eliminateFirstAndGetCodePoints(map);
+        }
+        return codePoints;
+    }
 
+    private static Set<String> asSet(Map<?, List<String>> map) {
+        final Set<String> flat = new HashSet<>();
+        for (List<String> value : map.values()) {
+            flat.addAll(value);
+        }
+        return flat;
+    }
 
-	private static boolean containsOnlyAmbiguousCyrillic(Collection<String> codePoints) {
-		for (String codePoint : codePoints) {
-			if (!AMBIGUOUS_CYRILLIC.contains(codePoint)) {
-				return false;
-			}
-		}
-		return true;
-	}
+    private static boolean containsOnlyAmbiguousCyrillic(Collection<String> codePoints) {
+        for (String codePoint : codePoints) {
+            if (!AMBIGUOUS_CYRILLIC.contains(codePoint)) {
+                return false;
+            }
+        }
+        return true;
+    }
 
-	private static PatternTuple find(Jid jid) {
-		synchronized (CACHE) {
-			PatternTuple pattern = CACHE.get(jid);
-			if (pattern != null) {
-				return pattern;
-			}
+    private static PatternTuple find(Jid jid) {
+        synchronized (CACHE) {
+            PatternTuple pattern = CACHE.get(jid);
+            if (pattern != null) {
+                return pattern;
+            }
             pattern = PatternTuple.of(jid);
-			CACHE.put(jid, pattern);
-			return pattern;
-		}
-	}
+            CACHE.put(jid, pattern);
+            return pattern;
+        }
+    }
 
-	private static Pattern create(Set<String> codePoints) {
-		final StringBuilder pattern = new StringBuilder();
-		for (String codePoint : codePoints) {
-			if (pattern.length() != 0) {
-				pattern.append('|');
-			}
-			pattern.append(Pattern.quote(codePoint));
-		}
-		return Pattern.compile(pattern.toString());
-	}
+    private static Pattern create(Set<String> codePoints) {
+        final StringBuilder pattern = new StringBuilder();
+        for (String codePoint : codePoints) {
+            if (pattern.length() != 0) {
+                pattern.append('|');
+            }
+            pattern.append(Pattern.quote(codePoint));
+        }
+        return Pattern.compile(pattern.toString());
+    }
 
-	private static class PatternTuple {
-		private final Pattern local;
-		private final List<Pattern> domain;
+    private static class PatternTuple {
+        private final Pattern local;
+        private final List<Pattern> domain;
 
-		private PatternTuple(Pattern local, List<Pattern> domain) {
-			this.local = local;
-			this.domain = domain;
-		}
+        private PatternTuple(Pattern local, List<Pattern> domain) {
+            this.local = local;
+            this.domain = domain;
+        }
 
-		private static PatternTuple of(Jid jid) {
-			final Pattern localPattern;
-			if (jid.getEscapedLocal() != null) {
-				localPattern = create(findIrregularCodePoints(jid.getEscapedLocal()));
-			} else {
-				localPattern = null;
-			}
-			String domain = jid.getDomain().toEscapedString();
-			final List<Pattern> domainPatterns = new ArrayList<>();
-			if (domain != null) {
-				for (String label : domain.split("\\.")) {
-					domainPatterns.add(create(findIrregularCodePoints(label)));
-				}
-			}
-			return new PatternTuple(localPattern, domainPatterns);
-		}
-	}
+        private static PatternTuple of(Jid jid) {
+            final Pattern localPattern;
+            if (jid.getLocal() != null) {
+                localPattern = create(findIrregularCodePoints(jid.getLocal()));
+            } else {
+                localPattern = null;
+            }
+            String domain = jid.getDomain().toString();
+            final List<Pattern> domainPatterns = new ArrayList<>();
+            if (domain != null) {
+                for (String label : domain.split("\\.")) {
+                    domainPatterns.add(create(findIrregularCodePoints(label)));
+                }
+            }
+            return new PatternTuple(localPattern, domainPatterns);
+        }
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/utils/JidHelper.java b/src/main/java/eu/siacs/conversations/utils/JidHelper.java
index e7443c8bb..aac7d156b 100644
--- a/src/main/java/eu/siacs/conversations/utils/JidHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/JidHelper.java
@@ -29,22 +29,19 @@
 
 package eu.siacs.conversations.utils;
 
-
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.xmpp.Jid;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.xmpp.InvalidJid;
-import eu.siacs.conversations.xmpp.Jid;
-
 public class JidHelper {
 
     private static final List<String> LOCAL_PART_BLACKLIST = Arrays.asList("xmpp", "jabber", "me");
 
     public static String localPartOrFallback(Jid jid) {
         if (LOCAL_PART_BLACKLIST.contains(jid.getLocal().toLowerCase(Locale.ENGLISH))) {
-            final String domain = jid.getDomain().toEscapedString();
+            final String domain = jid.getDomain().toString();
             final int index = domain.indexOf('.');
             return index > 1 ? domain.substring(0, index) : domain;
         } else {
@@ -52,16 +49,7 @@ public class JidHelper {
         }
     }
 
-    public static Jid parseOrFallbackToInvalid(String jid) {
-        try {
-            return Jid.of(jid);
-        } catch (IllegalArgumentException e) {
-            return InvalidJid.of(jid, true);
-        }
-    }
-
     public static boolean isQuicksyDomain(final Jid jid) {
         return Config.QUICKSY_DOMAIN != null && Config.QUICKSY_DOMAIN.equals(jid.getDomain());
     }
-
 }
diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
index 6c3075be9..b6022cb69 100644
--- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java
+++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
@@ -1,14 +1,12 @@
 package eu.siacs.conversations.utils;
 
 import android.net.Uri;
-
 import androidx.annotation.NonNull;
-
 import com.google.common.base.CharMatcher;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-
+import eu.siacs.conversations.xmpp.Jid;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.ArrayList;
@@ -18,8 +16,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-import eu.siacs.conversations.xmpp.Jid;
-
 public class XmppUri {
 
     public static final String ACTION_JOIN = "join";
@@ -42,8 +38,8 @@ public class XmppUri {
             parse(Uri.parse(uri));
         } catch (IllegalArgumentException e) {
             try {
-                jid = Jid.ofEscaped(uri).asBareJid().toEscapedString();
-            } catch (IllegalArgumentException e2) {
+                jid = Jid.of(uri).asBareJid().toString();
+            } catch (final IllegalArgumentException e2) {
                 jid = null;
             }
         }
@@ -60,7 +56,8 @@ public class XmppUri {
 
     private static Map<String, String> parseParameters(final String query, final char seperator) {
         final ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
-        final String[] pairs = query == null ? new String[0] : query.split(String.valueOf(seperator));
+        final String[] pairs =
+                query == null ? new String[0] : query.split(String.valueOf(seperator));
         for (String pair : pairs) {
             final String[] parts = pair.split("=", 2);
             if (parts.length == 0) {
@@ -94,7 +91,7 @@ public class XmppUri {
                     final int id = Integer.parseInt(key.substring(OMEMO_URI_PARAM.length()));
                     builder.add(new Fingerprint(FingerprintType.OMEMO, value, id));
                 } catch (Exception e) {
-                    //ignoring invalid device id
+                    // ignoring invalid device id
                 }
             } else if ("omemo".equals(key)) {
                 builder.add(new Fingerprint(FingerprintType.OMEMO, value, 0));
@@ -103,7 +100,8 @@ public class XmppUri {
         return builder.build();
     }
 
-    public static String getFingerprintUri(final String base, final List<XmppUri.Fingerprint> fingerprints, char separator) {
+    public static String getFingerprintUri(
+            final String base, final List<XmppUri.Fingerprint> fingerprints, char separator) {
         final StringBuilder builder = new StringBuilder(base);
         builder.append('?');
         for (int i = 0; i < fingerprints.size(); ++i) {
@@ -145,8 +143,8 @@ public class XmppUri {
             if (segments.size() >= 2 && segments.get(1).contains("@")) {
                 // sample : https://conversations.im/i/foo@bar.com
                 try {
-                    jid = Jid.ofEscaped(lameUrlDecode(segments.get(1))).toEscapedString();
-                } catch (Exception e) {
+                    jid = Jid.of(lameUrlDecode(segments.get(1))).toString();
+                } catch (final Exception e) {
                     jid = null;
                 }
             } else if (segments.size() >= 3) {
@@ -172,7 +170,8 @@ public class XmppUri {
                 }
             }
             this.fingerprints = parseFingerprints(parameters);
-        } else if ("imto".equalsIgnoreCase(scheme) && Arrays.asList("xmpp", "jabber").contains(uri.getHost())) {
+        } else if ("imto".equalsIgnoreCase(scheme)
+                && Arrays.asList("xmpp", "jabber").contains(uri.getHost())) {
             // sample: imto://xmpp/foo@bar.com
             try {
                 jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1].trim();
@@ -195,15 +194,18 @@ public class XmppUri {
 
     public boolean isAction(final String action) {
         return Collections2.transform(
-                parameters.keySet(),
-                s -> CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).retainFrom(s)
-        ).contains(action);
+                        parameters.keySet(),
+                        s ->
+                                CharMatcher.inRange('a', 'z')
+                                        .or(CharMatcher.inRange('A', 'Z'))
+                                        .retainFrom(s))
+                .contains(action);
     }
 
     public Jid getJid() {
         try {
-            return this.jid == null ? null : Jid.ofEscaped(this.jid);
-        } catch (IllegalArgumentException e) {
+            return this.jid == null ? null : Jid.ofUserInput(this.jid);
+        } catch (final IllegalArgumentException e) {
             return null;
         }
     }
@@ -213,9 +215,9 @@ public class XmppUri {
             return false;
         }
         try {
-            Jid.ofEscaped(jid);
+            Jid.ofUserInput(jid);
             return true;
-        } catch (IllegalArgumentException e) {
+        } catch (final IllegalArgumentException e) {
             return false;
         }
     }
@@ -237,7 +239,7 @@ public class XmppUri {
     }
 
     public boolean hasFingerprints() {
-        return fingerprints.size() > 0;
+        return !fingerprints.isEmpty();
     }
 
     public enum FingerprintType {
diff --git a/src/main/java/eu/siacs/conversations/worker/ExportBackupWorker.java b/src/main/java/eu/siacs/conversations/worker/ExportBackupWorker.java
index 02fc16326..d848ca54e 100644
--- a/src/main/java/eu/siacs/conversations/worker/ExportBackupWorker.java
+++ b/src/main/java/eu/siacs/conversations/worker/ExportBackupWorker.java
@@ -13,19 +13,16 @@ import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.core.app.NotificationCompat;
 import androidx.work.ForegroundInfo;
 import androidx.work.WorkManager;
 import androidx.work.Worker;
 import androidx.work.WorkerParameters;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.gson.stream.JsonWriter;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
@@ -36,7 +33,6 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
 import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.utils.BackupFileHeader;
 import eu.siacs.conversations.utils.Compatibility;
-
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -56,7 +52,6 @@ import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 import java.util.zip.GZIPOutputStream;
-
 import javax.crypto.Cipher;
 import javax.crypto.CipherOutputStream;
 import javax.crypto.NoSuchPaddingException;
@@ -162,7 +157,8 @@ public class ExportBackupWorker extends Worker {
                 Log.d(
                         Config.LOGTAG,
                         String.format(
-                                "skipping backup for %s because password is empty. unable to encrypt",
+                                "skipping backup for %s because password is empty. unable to"
+                                        + " encrypt",
                                 account.getJid().asBareJid()));
                 count++;
                 continue;
@@ -170,7 +166,7 @@ public class ExportBackupWorker extends Worker {
             final String filename =
                     String.format(
                             "%s.%s.ceb",
-                            account.getJid().asBareJid().toEscapedString(),
+                            account.getJid().asBareJid().toString(),
                             DATE_FORMAT.format(new Date()));
             final File file = new File(FileBackend.getBackupDirectory(context), filename);
             try {
@@ -379,7 +375,9 @@ public class ExportBackupWorker extends Worker {
                 getApplicationContext().getSystemService(NotificationManager.class);
         try (final Cursor cursor =
                 db.rawQuery(
-                        "select messages.* from messages join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=?",
+                        "select messages.* from messages join conversations on"
+                                + " conversations.uuid=messages.conversationUuid where"
+                                + " conversations.accountUuid=?",
                         new String[] {uuid})) {
             final int size = cursor != null ? cursor.getCount() : 0;
             Log.d(Config.LOGTAG, "exporting " + size + " messages for account " + uuid);
diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java
index 20db7f878..1b91eeaae 100644
--- a/src/main/java/eu/siacs/conversations/xml/Element.java
+++ b/src/main/java/eu/siacs/conversations/xml/Element.java
@@ -1,20 +1,16 @@
 package eu.siacs.conversations.xml;
 
 import androidx.annotation.NonNull;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.primitives.Ints;
 import com.google.common.primitives.Longs;
-
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.List;
-
 import eu.siacs.conversations.utils.XmlHelper;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
 import im.conversations.android.xmpp.model.stanza.Message;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
 
 public class Element {
     private final String name;
@@ -133,7 +129,7 @@ public class Element {
 
     public Element setAttribute(String name, Jid value) {
         if (name != null && value != null) {
-            this.attributes.put(name, value.toEscapedString());
+            this.attributes.put(name, value.toString());
         }
         return this;
     }
@@ -172,16 +168,12 @@ public class Element {
         return Optional.fromNullable(Ints.tryParse(value));
     }
 
-    public Jid getAttributeAsJid(String name) {
+    public Jid getAttributeAsJid(final String name) {
         final String jid = this.getAttribute(name);
-        if (jid != null && !jid.isEmpty()) {
-            try {
-                return Jid.ofEscaped(jid);
-            } catch (final IllegalArgumentException e) {
-                return InvalidJid.of(jid, this instanceof Message);
-            }
+        if (Strings.isNullOrEmpty(jid)) {
+            return null;
         }
-        return null;
+        return Jid.ofOrInvalid(jid, this instanceof Message);
     }
 
     public Hashtable<String, String> getAttributes() {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java b/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java
deleted file mode 100644
index 4e3092821..000000000
--- a/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2018, Daniel Gultsch All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation and/or
- * other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its contributors
- * may be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package eu.siacs.conversations.xmpp;
-
-import androidx.annotation.NonNull;
-
-import im.conversations.android.xmpp.model.stanza.Stanza;
-
-public class InvalidJid implements Jid {
-
-	private final String value;
-
-	private InvalidJid(String jid) {
-		this.value = jid;
-	}
-
-	public  static Jid of(String jid, boolean fallback) {
-		final int pos = jid.indexOf('/');
-		if (fallback && pos >= 0 && jid.length() >= pos + 1) {
-			if (jid.substring(pos+1).trim().isEmpty()) {
-				return Jid.ofEscaped(jid.substring(0,pos));
-			}
-		}
-		return new InvalidJid(jid);
-	}
-
-	@Override
-	@NonNull
-	public String toString() {
-		return value;
-	}
-
-	@Override
-	public boolean isFullJid() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public boolean isBareJid() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public boolean isDomainJid() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public Jid asBareJid() {
-		throw new AssertionError("Not implemented");
-	}
-
-
-	@Override
-	public Jid withResource(CharSequence charSequence) {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public String getLocal() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public String getEscapedLocal() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public Jid getDomain() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public String getResource() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public String toEscapedString() {
-		throw new AssertionError("Not implemented");
-	}
-
-	@Override
-	public int length() {
-		return value.length();
-	}
-
-	@Override
-	public char charAt(int index) {
-		return value.charAt(index);
-	}
-
-	@Override
-	public CharSequence subSequence(int start, int end) {
-		return value.subSequence(start, end);
-	}
-
-	@Override
-	public int compareTo(@NonNull Jid o) {
-		throw new AssertionError("Not implemented");
-	}
-
-	public static Jid getNullForInvalid(Jid jid) {
-		if (jid instanceof InvalidJid) {
-			return null;
-		} else {
-			return jid;
-		}
-	}
-
-	public static boolean isValid(Jid jid) {
-		return !(jid instanceof InvalidJid);
-	}
-
-	public static boolean hasValidFrom(Stanza stanza) {
-		final String from = stanza.getAttribute("from");
-		if (from == null) {
-			return false;
-		}
-		try {
-			Jid.ofEscaped(from);
-			return true;
-		} catch (IllegalArgumentException e) {
-			return false;
-		}
-	}
-}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/Jid.java
index 299c872b3..34fbb9abb 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/Jid.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/Jid.java
@@ -1,20 +1,19 @@
 package eu.siacs.conversations.xmpp;
 
+import androidx.annotation.NonNull;
+import com.google.common.base.CharMatcher;
+import im.conversations.android.xmpp.model.stanza.Stanza;
+import java.io.Serializable;
 import org.jxmpp.jid.impl.JidCreate;
 import org.jxmpp.jid.parts.Domainpart;
 import org.jxmpp.jid.parts.Localpart;
 import org.jxmpp.jid.parts.Resourcepart;
 import org.jxmpp.stringprep.XmppStringprepException;
 
-import java.io.Serializable;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public interface Jid extends Comparable<Jid>, Serializable, CharSequence {
-
-    Pattern JID = Pattern.compile("^((.*?)@)?([^/@]+)(/(.*))?$");
+public abstract class Jid implements Comparable<Jid>, Serializable, CharSequence {
 
-    static Jid of(CharSequence local, CharSequence domain, CharSequence resource) {
+    public static Jid of(
+            final CharSequence local, final CharSequence domain, final CharSequence resource) {
         if (local == null) {
             if (resource == null) {
                 return ofDomain(domain);
@@ -26,120 +25,312 @@ public interface Jid extends Comparable<Jid>, Serializable, CharSequence {
             return ofLocalAndDomain(local, domain);
         }
         try {
-            return new WrappedJid(JidCreate.entityFullFrom(
-                    Localpart.fromUnescaped(local.toString()),
-                    Domainpart.from(domain.toString()),
-                    Resourcepart.from(resource.toString())
-            ));
-        } catch (XmppStringprepException e) {
+            return new InternalRepresentation(
+                    JidCreate.entityFullFrom(
+                            Localpart.from(local.toString()),
+                            Domainpart.from(domain.toString()),
+                            Resourcepart.from(resource.toString())));
+        } catch (final XmppStringprepException e) {
             throw new IllegalArgumentException(e);
         }
     }
 
-    static Jid ofEscaped(CharSequence local, CharSequence domain, CharSequence resource) {
+    public static Jid ofDomain(final CharSequence domain) {
         try {
-            if (resource == null) {
-                return new WrappedJid(
-                        JidCreate.bareFrom(
-                                Localpart.from(local.toString()),
-                                Domainpart.from(domain.toString())
-                        )
-                );
-            }
-            return new WrappedJid(JidCreate.entityFullFrom(
-                    Localpart.from(local.toString()),
-                    Domainpart.from(domain.toString()),
-                    Resourcepart.from(resource.toString())
-            ));
-        } catch (XmppStringprepException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-
-    static Jid ofDomain(CharSequence domain) {
-        try {
-            return new WrappedJid(JidCreate.domainBareFrom(domain));
-        } catch (XmppStringprepException e) {
+            return new InternalRepresentation(JidCreate.domainBareFrom(domain));
+        } catch (final XmppStringprepException e) {
             throw new IllegalArgumentException(e);
         }
     }
 
-    static Jid ofLocalAndDomain(CharSequence local, CharSequence domain) {
+    public static Jid ofLocalAndDomain(final CharSequence local, final CharSequence domain) {
         try {
-            return new WrappedJid(
+            return new InternalRepresentation(
                     JidCreate.bareFrom(
-                            Localpart.fromUnescaped(local.toString()),
-                            Domainpart.from(domain.toString())
-                    )
-            );
-        } catch (XmppStringprepException e) {
+                            Localpart.from(local.toString()), Domainpart.from(domain.toString())));
+        } catch (final XmppStringprepException e) {
             throw new IllegalArgumentException(e);
         }
     }
 
-    static Jid ofDomainAndResource(CharSequence domain, CharSequence resource) {
+    public static Jid ofDomainAndResource(CharSequence domain, CharSequence resource) {
         try {
-            return new WrappedJid(
+            return new InternalRepresentation(
                     JidCreate.domainFullFrom(
                             Domainpart.from(domain.toString()),
-                            Resourcepart.from(resource.toString())
-                    ));
-        } catch (XmppStringprepException e) {
+                            Resourcepart.from(resource.toString())));
+        } catch (final XmppStringprepException e) {
             throw new IllegalArgumentException(e);
         }
     }
 
-    static Jid ofLocalAndDomainEscaped(CharSequence local, CharSequence domain) {
+    public static Jid of(final CharSequence input) {
+        if (input instanceof Jid jid) {
+            return jid;
+        }
         try {
-            return new WrappedJid(
-                    JidCreate.bareFrom(
-                            Localpart.from(local.toString()),
-                            Domainpart.from(domain.toString())
-                    )
-            );
-        } catch (XmppStringprepException e) {
+            return new InternalRepresentation(JidCreate.from(input));
+        } catch (final XmppStringprepException e) {
             throw new IllegalArgumentException(e);
         }
     }
 
-    static Jid of(CharSequence jid) {
-        if (jid instanceof Jid) {
-            return (Jid) jid;
-        }
-        Matcher matcher = JID.matcher(jid);
-        if (matcher.matches()) {
-            return of(matcher.group(2), matcher.group(3), matcher.group(5));
-        } else {
-            throw new IllegalArgumentException("Could not parse JID: " + jid);
+    public static Jid ofUserInput(final CharSequence input) {
+        final var jid = of(input);
+        if (CharMatcher.is('@').matchesAnyOf(jid.getDomain())) {
+            throw new IllegalArgumentException("Domain should not contain @");
         }
+        return jid;
+    }
+
+    public static Jid ofOrInvalid(final String input) {
+        return ofOrInvalid(input, false);
     }
 
-    static Jid ofEscaped(CharSequence jid) {
+    /**
+     *
+     * @param jid a string representation of the jid to parse
+     * @param fallback indicates whether an attempt should be made to parse a bare version of the jid
+     * @return an instance of Jid; may be Jid.Invalid
+     */
+    public static Jid ofOrInvalid(final String jid, final boolean fallback) {
         try {
-            return new WrappedJid(JidCreate.from(jid));
-        } catch (final XmppStringprepException e) {
-            throw new IllegalArgumentException(e);
+            return Jid.of(jid);
+        } catch (final IllegalArgumentException e) {
+            return Jid.invalidOf(jid, fallback);
         }
     }
 
-    boolean isFullJid();
+    private static Jid invalidOf(final String jid, boolean fallback) {
+        final int pos = jid.indexOf('/');
+        if (fallback && pos >= 0 && jid.length() >= pos + 1) {
+            if (jid.substring(pos + 1).trim().isEmpty()) {
+                return Jid.of(jid.substring(0, pos));
+            }
+        }
+        return new Invalid(jid);
+    }
+
+    public abstract boolean isFullJid();
+
+    public abstract boolean isBareJid();
+
+    public abstract boolean isDomainJid();
+
+    public abstract Jid asBareJid();
 
-    boolean isBareJid();
+    public abstract Jid withResource(CharSequence resource);
 
-    boolean isDomainJid();
+    public abstract String getLocal();
 
-    Jid asBareJid();
+    public abstract Jid getDomain();
 
-    Jid withResource(CharSequence resource);
+    public abstract String getResource();
 
-    String getLocal();
+    private static class InternalRepresentation extends Jid {
+        private final org.jxmpp.jid.Jid inner;
 
-    String getEscapedLocal();
+        private InternalRepresentation(final org.jxmpp.jid.Jid inner) {
+            this.inner = inner;
+        }
+
+        @Override
+        public boolean isFullJid() {
+            return inner.isEntityFullJid() || inner.isDomainFullJid();
+        }
 
-    Jid getDomain();
+        @Override
+        public boolean isBareJid() {
+            return inner.isDomainBareJid() || inner.isEntityBareJid();
+        }
 
-    String getResource();
+        @Override
+        public boolean isDomainJid() {
+            return inner.isDomainBareJid() || inner.isDomainFullJid();
+        }
+
+        @Override
+        public Jid asBareJid() {
+            return new InternalRepresentation(inner.asBareJid());
+        }
+
+        @Override
+        public Jid withResource(CharSequence resource) {
+            final Localpart localpart = inner.getLocalpartOrNull();
+            try {
+                final Resourcepart resourcepart = Resourcepart.from(resource.toString());
+                if (localpart == null) {
+                    return new InternalRepresentation(
+                            JidCreate.domainFullFrom(inner.getDomain(), resourcepart));
+                } else {
+                    return new InternalRepresentation(
+                            JidCreate.fullFrom(localpart, inner.getDomain(), resourcepart));
+                }
+            } catch (XmppStringprepException e) {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
+        @Override
+        public String getLocal() {
+            final Localpart localpart = inner.getLocalpartOrNull();
+            return localpart == null ? null : localpart.toString();
+        }
+
+        @Override
+        public Jid getDomain() {
+            return new InternalRepresentation(inner.asDomainBareJid());
+        }
+
+        @Override
+        public String getResource() {
+            final Resourcepart resourcepart = inner.getResourceOrNull();
+            return resourcepart == null ? null : resourcepart.toString();
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return inner.toString();
+        }
+
+        @Override
+        public int length() {
+            return inner.length();
+        }
 
-    String toEscapedString();
+        @Override
+        public char charAt(int i) {
+            return inner.charAt(i);
+        }
+
+        @NonNull
+        @Override
+        public CharSequence subSequence(int i, int i1) {
+            return inner.subSequence(i, i1);
+        }
+
+        @Override
+        public int compareTo(Jid jid) {
+            if (jid instanceof InternalRepresentation) {
+                return inner.compareTo(((InternalRepresentation) jid).inner);
+            } else {
+                return 0;
+            }
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            InternalRepresentation that = (InternalRepresentation) o;
+            return inner.equals(that.inner);
+        }
+
+        @Override
+        public int hashCode() {
+            return inner.hashCode();
+        }
+    }
+
+    public static class Invalid extends Jid {
+
+        private final String value;
+
+        private Invalid(final String jid) {
+            this.value = jid;
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return value;
+        }
+
+        @Override
+        public boolean isFullJid() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public boolean isBareJid() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public boolean isDomainJid() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public Jid asBareJid() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public Jid withResource(CharSequence charSequence) {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public String getLocal() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public Jid getDomain() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public String getResource() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public int length() {
+            return value.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return value.charAt(index);
+        }
+
+        @NonNull
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return value.subSequence(start, end);
+        }
+
+        @Override
+        public int compareTo(@NonNull Jid o) {
+            throw new AssertionError("Not implemented");
+        }
+
+        public static Jid getNullForInvalid(final Jid jid) {
+            if (jid instanceof Invalid) {
+                return null;
+            } else {
+                return jid;
+            }
+        }
+
+        public static boolean isValid(Jid jid) {
+            return !(jid instanceof Invalid);
+        }
+
+        public static boolean hasValidFrom(final Stanza stanza) {
+            final String from = stanza.getAttribute("from");
+            if (from == null) {
+                return false;
+            }
+            try {
+                Jid.of(from);
+                return true;
+            } catch (final IllegalArgumentException e) {
+                return false;
+            }
+        }
+    }
 }
diff --git a/src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java b/src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java
deleted file mode 100644
index 08fb6e6dc..000000000
--- a/src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package eu.siacs.conversations.xmpp;
-
-
-import androidx.annotation.NonNull;
-
-import org.jxmpp.jid.Jid;
-import org.jxmpp.jid.impl.JidCreate;
-import org.jxmpp.jid.parts.Localpart;
-import org.jxmpp.jid.parts.Resourcepart;
-import org.jxmpp.stringprep.XmppStringprepException;
-
-
-public class WrappedJid implements eu.siacs.conversations.xmpp.Jid {
-    private final Jid inner;
-
-    WrappedJid(Jid inner) {
-        this.inner = inner;
-    }
-
-    @Override
-    public boolean isFullJid() {
-        return inner.isEntityFullJid() || inner.isDomainFullJid();
-    }
-
-    @Override
-    public boolean isBareJid() {
-        return inner.isDomainBareJid() || inner.isEntityBareJid();
-    }
-
-    @Override
-    public boolean isDomainJid() {
-        return inner.isDomainBareJid() || inner.isDomainFullJid();
-    }
-
-    @Override
-    public eu.siacs.conversations.xmpp.Jid asBareJid() {
-        return new WrappedJid(inner.asBareJid());
-    }
-
-    @Override
-    public eu.siacs.conversations.xmpp.Jid withResource(CharSequence resource) {
-        final Localpart localpart = inner.getLocalpartOrNull();
-        try {
-            final Resourcepart resourcepart = Resourcepart.from(resource.toString());
-            if (localpart == null) {
-                return new WrappedJid(JidCreate.domainFullFrom(inner.getDomain(),resourcepart));
-            } else {
-                return new WrappedJid(
-                        JidCreate.fullFrom(
-                                localpart,
-                                inner.getDomain(),
-                                resourcepart
-                        ));
-            }
-        } catch (XmppStringprepException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    @Override
-    public String getLocal() {
-        final Localpart localpart = inner.getLocalpartOrNull();
-        return localpart == null ? null : localpart.asUnescapedString();
-    }
-
-    @Override
-    public String getEscapedLocal() {
-        final Localpart localpart = inner.getLocalpartOrNull();
-        return localpart == null ? null : localpart.toString();
-    }
-
-    @Override
-    public eu.siacs.conversations.xmpp.Jid getDomain() {
-        return new WrappedJid(inner.asDomainBareJid());
-    }
-
-    @Override
-    public String getResource() {
-        final Resourcepart resourcepart = inner.getResourceOrNull();
-        return resourcepart == null ? null : resourcepart.toString();
-    }
-
-    @Override
-    public String toEscapedString() {
-        return inner.toString();
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        return inner.asUnescapedString();
-    }
-
-    @Override
-    public int length() {
-        return inner.length();
-    }
-
-    @Override
-    public char charAt(int i) {
-        return inner.charAt(i);
-    }
-
-    @Override
-    public CharSequence subSequence(int i, int i1) {
-        return inner.subSequence(i,i1);
-    }
-
-    @Override
-    public int compareTo(eu.siacs.conversations.xmpp.Jid jid) {
-        if (jid instanceof WrappedJid) {
-            return inner.compareTo(((WrappedJid) jid).inner);
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        WrappedJid that = (WrappedJid) o;
-        return inner.equals(that.inner);
-    }
-
-    @Override
-    public int hashCode() {
-        return inner.hashCode();
-    }
-}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 6ce05e378..d02d533ca 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -2319,7 +2319,7 @@ public class XmppConnection implements Runnable {
                         for (final Element element : elements) {
                             if (element.getName().equals("item")) {
                                 final Jid jid =
-                                        InvalidJid.getNullForInvalid(
+                                        Jid.Invalid.getNullForInvalid(
                                                 element.getAttributeAsJid("jid"));
                                 if (jid != null && !jid.equals(account.getDomain())) {
                                     items.add(jid);
@@ -2484,7 +2484,7 @@ public class XmppConnection implements Runnable {
         final Tag stream = Tag.start("stream:stream");
         stream.setAttribute("to", account.getServer());
         if (from) {
-            stream.setAttribute("from", account.getJid().asBareJid().toEscapedString());
+            stream.setAttribute("from", account.getJid().asBareJid().toString());
         }
         stream.setAttribute("version", "1.0");
         stream.setAttribute("xml:lang", LocalizedContent.STREAM_LANGUAGE);
@@ -2703,7 +2703,7 @@ public class XmppConnection implements Runnable {
 
     public List<String> getMucServersWithholdAccount() {
         final List<String> servers = getMucServers();
-        servers.remove(account.getDomain().toEscapedString());
+        servers.remove(account.getDomain().toString());
         return servers;
     }
 
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 1f93ed924..d01880ab8 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -4,9 +4,7 @@ import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.util.Base64;
 import android.util.Log;
-
 import androidx.annotation.Nullable;
-
 import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -15,7 +13,6 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ComparisonChain;
 import com.google.common.collect.ImmutableSet;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
@@ -39,10 +36,8 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
 import eu.siacs.conversations.xmpp.jingle.transports.InbandBytestreamsTransport;
 import eu.siacs.conversations.xmpp.jingle.transports.Transport;
-
 import im.conversations.android.xmpp.model.jingle.Jingle;
 import im.conversations.android.xmpp.model.stanza.Iq;
-
 import java.lang.ref.WeakReference;
 import java.security.SecureRandom;
 import java.util.Arrays;
@@ -353,7 +348,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                     Log.d(
                             Config.LOGTAG,
                             id.account.getJid().asBareJid()
-                                    + ": updated previous busy because call got picked up by another device");
+                                    + ": updated previous busy because call got picked up by"
+                                    + " another device");
                     mXmppConnectionService.getNotificationService().clearMissedCall(previousBusy);
                     return;
                 }
@@ -393,15 +389,14 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                     final String theirSessionId = id.sessionId;
                     if (ComparisonChain.start()
                                     .compare(ourSessionId, theirSessionId)
-                                    .compare(
-                                            account.getJid().toEscapedString(),
-                                            id.with.toEscapedString())
+                                    .compare(account.getJid().toString(), id.with.toString())
                                     .result()
                             > 0) {
                         Log.d(
                                 Config.LOGTAG,
                                 account.getJid().asBareJid()
-                                        + ": our session lost tie break. automatically accepting their session. winning Session="
+                                        + ": our session lost tie break. automatically accepting"
+                                        + " their session. winning Session="
                                         + theirSessionId);
                         // TODO a retract for this reason should probably include some indication of
                         // tie break
@@ -417,7 +412,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                         Log.d(
                                 Config.LOGTAG,
                                 account.getJid().asBareJid()
-                                        + ": our session won tie break. waiting for other party to accept. winningSession="
+                                        + ": our session won tie break. waiting for other party to"
+                                        + " accept. winningSession="
                                         + ourSessionId);
                         // TODO reject their session with <tie-break/>?
                     }
@@ -453,7 +449,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                         Log.d(
                                 Config.LOGTAG,
                                 id.account.getJid().asBareJid()
-                                        + ": ignoring proposal because busy on this device but there are other devices");
+                                        + ": ignoring proposal because busy on this device but"
+                                        + " there are other devices");
                     }
                 } else {
                     final JingleRtpConnection rtpConnection =
@@ -772,12 +769,14 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                 if (hasMatchingRtpSession(account, with, media)) {
                     Log.d(
                             Config.LOGTAG,
-                            "ignoring request to propose jingle session because the other party already created one for us");
+                            "ignoring request to propose jingle session because the other party"
+                                    + " already created one for us");
                     // TODO return something that we can parse the connection of of
                     return null;
                 }
                 throw new IllegalStateException(
-                        "There is already a running RTP session. This should have been caught by the UI");
+                        "There is already a running RTP session. This should have been caught by"
+                                + " the UI");
             }
             final CallIntegration callIntegration =
                     new CallIntegration(mXmppConnectionService.getApplicationContext());
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
index b4f1882f5..10a6d4ba6 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
@@ -4,10 +4,8 @@ import android.content.Intent;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -24,7 +22,6 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
-
 import eu.siacs.conversations.BuildConfig;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -47,15 +44,8 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Proceed;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Propose;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
-
 import im.conversations.android.xmpp.model.jingle.Jingle;
 import im.conversations.android.xmpp.model.stanza.Iq;
-
-import org.webrtc.EglBase;
-import org.webrtc.IceCandidate;
-import org.webrtc.PeerConnection;
-import org.webrtc.VideoTrack;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -67,6 +57,10 @@ import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import org.webrtc.EglBase;
+import org.webrtc.IceCandidate;
+import org.webrtc.PeerConnection;
+import org.webrtc.VideoTrack;
 
 public class JingleRtpConnection extends AbstractJingleConnection
         implements WebRTCWrapper.EventCallback, CallIntegration.Callback, OngoingRtpSession {
@@ -278,7 +272,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": PeerConnection was not initialized when processing transport info. this usually indicates a race condition that can be ignored");
+                            + ": PeerConnection was not initialized when processing transport info."
+                            + " this usually indicates a race condition that can be ignored");
         }
     }
 
@@ -625,7 +620,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.d(
                     Config.LOGTAG,
                     id.getAccount().getJid().asBareJid()
-                            + ": unable to rollback local description after receiving content-reject",
+                            + ": unable to rollback local description after receiving"
+                            + " content-reject",
                     cause);
             webRTCWrapper.close();
             sendSessionTerminate(Reason.FAILED_APPLICATION, cause.getMessage());
@@ -694,7 +690,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.d(
                     Config.LOGTAG,
                     id.getAccount().getJid().asBareJid()
-                            + ": unable to rollback local description after trying to retract content-add",
+                            + ": unable to rollback local description after trying to retract"
+                            + " content-add",
                     cause);
             webRTCWrapper.close();
             sendSessionTerminate(Reason.FAILED_APPLICATION, cause.getMessage());
@@ -773,14 +770,16 @@ public class JingleRtpConnection extends AbstractJingleConnection
                                 Log.d(
                                         Config.LOGTAG,
                                         id.account.getJid().asBareJid()
-                                                + ": remote has accepted our upgrade to senders=both");
+                                                + ": remote has accepted our upgrade to"
+                                                + " senders=both");
                                 acceptContentAdd(
                                         ContentAddition.summary(modifiedSenders), modifiedSenders);
                             } else {
                                 Log.d(
                                         Config.LOGTAG,
                                         id.account.getJid().asBareJid()
-                                                + ": remote has rejected our upgrade to senders=both");
+                                                + ": remote has rejected our upgrade to"
+                                                + " senders=both");
                                 acceptContentAdd(contentAddition, incomingContentAdd);
                             }
                         });
@@ -1072,7 +1071,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
+                            + ": no identification tags found in initial offer. we won't be able to"
+                            + " calculate mLineIndices");
         }
         return identificationTags;
     }
@@ -1169,7 +1169,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 sendSessionTerminate(
                         Reason.SECURITY_ERROR,
                         String.format(
-                                "Your session proposal (Jingle Message Initiation) included media %s but your session-initiate was %s",
+                                "Your session proposal (Jingle Message Initiation) included media"
+                                        + " %s but your session-initiate was %s",
                                 this.proposedMedia, contentMap.getMedia()));
                 return;
             }
@@ -1254,7 +1255,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             sendSessionTerminate(
                     Reason.SECURITY_ERROR,
                     String.format(
-                            "Your session-included included media %s but our session-initiate was %s",
+                            "Your session-included included media %s but our session-initiate was"
+                                    + " %s",
                             this.proposedMedia, contentMap.getMedia()));
             return;
         }
@@ -1342,7 +1344,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": ICE servers got discovered when session was already terminated. nothing to do.");
+                            + ": ICE servers got discovered when session was already terminated."
+                            + " nothing to do.");
             return;
         }
         final boolean includeCandidates = remoteHasSdpOfferAnswer();
@@ -1445,7 +1448,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": preparing session accept was too slow. already terminated. nothing to do.");
+                            + ": preparing session accept was too slow. already terminated. nothing"
+                            + " to do.");
             return;
         }
         transitionOrThrow(State.SESSION_ACCEPTED);
@@ -1488,10 +1492,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
                         + ": delivered message to JingleRtpConnection "
                         + message);
         switch (message.getName()) {
-            case "propose" -> receivePropose(
-                    from, Propose.upgrade(message), serverMessageId, timestamp);
-            case "proceed" -> receiveProceed(
-                    from, Proceed.upgrade(message), serverMessageId, timestamp);
+            case "propose" ->
+                    receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
+            case "proceed" ->
+                    receiveProceed(from, Proceed.upgrade(message), serverMessageId, timestamp);
             case "retract" -> receiveRetract(from, serverMessageId, timestamp);
             case "reject" -> receiveReject(from, serverMessageId, timestamp);
             case "accept" -> receiveAccept(from, serverMessageId, timestamp);
@@ -1605,7 +1609,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.d(
                     Config.LOGTAG,
                     id.account.getJid()
-                            + ": received reject while in SESSION_INITIATED_PRE_APPROVED. callee reconsidered before receiving session-init");
+                            + ": received reject while in SESSION_INITIATED_PRE_APPROVED. callee"
+                            + " reconsidered before receiving session-init");
             closeTransitionLogFinish(State.TERMINATED_DECLINED_OR_BUSY);
             return;
         }
@@ -1727,7 +1732,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                             Log.d(
                                     Config.LOGTAG,
                                     id.account.getJid().asBareJid()
-                                            + ": remote party signaled support for OMEMO verification but we have OMEMO disabled");
+                                            + ": remote party signaled support for OMEMO"
+                                            + " verification but we have OMEMO disabled");
                         }
                         this.omemoVerification.setDeviceId(null);
                     }
@@ -1822,7 +1828,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": ICE servers got discovered when session was already terminated. nothing to do.");
+                            + ": ICE servers got discovered when session was already terminated."
+                            + " nothing to do.");
             return;
         }
         final boolean includeCandidates = remoteHasSdpOfferAnswer();
@@ -1917,7 +1924,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": preparing session was too slow. already terminated. nothing to do.");
+                            + ": preparing session was too slow. already terminated. nothing to"
+                            + " do.");
             return;
         }
         this.transitionOrThrow(targetState);
@@ -1956,7 +1964,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                         Log.w(
                                 Config.LOGTAG,
                                 id.account.getJid().asBareJid()
-                                        + ": unable to use OMEMO DTLS verification on outgoing session initiate. falling back",
+                                        + ": unable to use OMEMO DTLS verification on outgoing"
+                                        + " session initiate. falling back",
                                 e);
                         return rtpContentMap;
                     },
@@ -2070,9 +2079,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
             case CONNECTED -> RtpEndUserState.CONNECTED;
             case NEW, CONNECTING -> RtpEndUserState.CONNECTING;
             case CLOSED -> RtpEndUserState.ENDING_CALL;
-            default -> zeroDuration()
-                    ? RtpEndUserState.CONNECTIVITY_ERROR
-                    : RtpEndUserState.RECONNECTING;
+            default ->
+                    zeroDuration()
+                            ? RtpEndUserState.CONNECTIVITY_ERROR
+                            : RtpEndUserState.RECONNECTING;
         };
     }
 
@@ -2116,13 +2126,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
             }
             case TERMINATED_SUCCESS -> this.callIntegration.success();
             case ACCEPTED -> this.callIntegration.accepted();
-            case RETRACTED, RETRACTED_RACED, TERMINATED_CANCEL_OR_TIMEOUT -> this.callIntegration
-                    .retracted();
+            case RETRACTED, RETRACTED_RACED, TERMINATED_CANCEL_OR_TIMEOUT ->
+                    this.callIntegration.retracted();
             case TERMINATED_CONNECTIVITY_ERROR,
-                    TERMINATED_APPLICATION_FAILURE,
-                    TERMINATED_SECURITY_ERROR -> this.callIntegration.error();
-            default -> throw new IllegalStateException(
-                    String.format("%s is not handled", this.state));
+                            TERMINATED_APPLICATION_FAILURE,
+                            TERMINATED_SECURITY_ERROR ->
+                    this.callIntegration.error();
+            default ->
+                    throw new IllegalStateException(String.format("%s is not handled", this.state));
         }
     }
 
@@ -2195,14 +2206,18 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 cancelRingingTimeout();
                 acceptCallFromSessionInitialized();
             }
-            case ACCEPTED -> Log.w(
-                    Config.LOGTAG,
-                    id.account.getJid().asBareJid()
-                            + ": the call has already been accepted  with another client. UI was just lagging behind");
-            case PROCEED, SESSION_ACCEPTED -> Log.w(
-                    Config.LOGTAG,
-                    id.account.getJid().asBareJid()
-                            + ": the call has already been accepted. user probably double tapped the UI");
+            case ACCEPTED ->
+                    Log.w(
+                            Config.LOGTAG,
+                            id.account.getJid().asBareJid()
+                                    + ": the call has already been accepted  with another client."
+                                    + " UI was just lagging behind");
+            case PROCEED, SESSION_ACCEPTED ->
+                    Log.w(
+                            Config.LOGTAG,
+                            id.account.getJid().asBareJid()
+                                    + ": the call has already been accepted. user probably double"
+                                    + " tapped the UI");
             default -> throw new IllegalStateException("Can not accept call from " + this.state);
         }
     }
@@ -2212,7 +2227,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": received rejectCall() when session has already been terminated. nothing to do");
+                            + ": received rejectCall() when session has already been terminated."
+                            + " nothing to do");
             return;
         }
         switch (this.state) {
@@ -2245,7 +2261,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             Log.w(
                     Config.LOGTAG,
                     id.account.getJid().asBareJid()
-                            + ": received endCall() when session has already been terminated. nothing to do");
+                            + ": received endCall() when session has already been terminated."
+                            + " nothing to do");
             return;
         }
         if (isInState(State.PROPOSED) && isResponder()) {
@@ -2447,7 +2464,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                     Log.d(
                             Config.LOGTAG,
                             id.account.getJid().asBareJid()
-                                    + ": not sending session-terminate after connectivity error because session is already in state "
+                                    + ": not sending session-terminate after connectivity error"
+                                    + " because session is already in state "
                                     + this.state);
                     return;
                 }
@@ -2649,7 +2667,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 Log.d(
                         Config.LOGTAG,
                         id.account.getJid().asBareJid()
-                                + ": no need to send session-terminate after failed connection. Other party already did");
+                                + ": no need to send session-terminate after failed connection."
+                                + " Other party already did");
                 return;
             }
             sendSessionTerminate(Reason.CONNECTIVITY_ERROR);
@@ -2704,7 +2723,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             // callback when the rtp session has already ended.
             Log.w(
                     Config.LOGTAG,
-                    "CallIntegration requested incoming call UI but session was already terminated");
+                    "CallIntegration requested incoming call UI but session was already"
+                            + " terminated");
             return;
         }
         // TODO apparently this can be called too early as well?
@@ -2736,8 +2756,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
         // we need to start the UI to a) show it and b) be able to ask for permissions
         final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class);
         intent.setAction(RtpSessionActivity.ACTION_ACCEPT_CALL);
-        intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().toEscapedString());
-        intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
+        intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().toString());
+        intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
         intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/SocksByteStreamsTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/SocksByteStreamsTransport.java
index 2925592ea..8cebf080f 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/SocksByteStreamsTransport.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/SocksByteStreamsTransport.java
@@ -1,10 +1,8 @@
 package eu.siacs.conversations.xmpp.jingle.transports;
 
 import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
 import com.google.common.base.Joiner;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Optional;
@@ -22,7 +20,6 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.utils.SocksSocketFactory;
 import eu.siacs.conversations.xml.Element;
@@ -33,7 +30,6 @@ import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
 import eu.siacs.conversations.xmpp.jingle.DirectConnectionUtils;
 import eu.siacs.conversations.xmpp.jingle.stanzas.SocksByteStreamsTransportInfo;
 import im.conversations.android.xmpp.model.stanza.Iq;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -104,8 +100,8 @@ public class SocksByteStreamsTransport implements Transport {
                                         .join(
                                                 Arrays.asList(
                                                         streamId,
-                                                        id.with.toEscapedString(),
-                                                        id.account.getJid().toEscapedString())),
+                                                        id.with.toString(),
+                                                        id.account.getJid().toString())),
                                 StandardCharsets.UTF_8)
                         .toString();
         final var ourDestination =
@@ -115,8 +111,8 @@ public class SocksByteStreamsTransport implements Transport {
                                         .join(
                                                 Arrays.asList(
                                                         streamId,
-                                                        id.account.getJid().toEscapedString(),
-                                                        id.with.toEscapedString())),
+                                                        id.account.getJid().toString(),
+                                                        id.with.toString())),
                                 StandardCharsets.UTF_8)
                         .toString();
 
@@ -255,7 +251,7 @@ public class SocksByteStreamsTransport implements Transport {
         final Element query = proxyActivation.addChild("query", Namespace.BYTE_STREAMS);
         query.setAttribute("sid", this.streamId);
         final Element activate = query.addChild("activate");
-        activate.setContent(id.with.toEscapedString());
+        activate.setContent(id.with.toString());
         xmppConnection.sendIqPacket(
                 proxyActivation,
                 (response) -> {
@@ -731,10 +727,12 @@ public class SocksByteStreamsTransport implements Transport {
                         && selectedByThemCandidatePriority > candidate.priority) {
                     Log.d(
                             Config.LOGTAG,
-                            "The candidate selected by peer had a higher priority then anything we could try");
+                            "The candidate selected by peer had a higher priority then anything we"
+                                    + " could try");
                     connectionFuture.setException(
                             new CandidateErrorException(
-                                    "The candidate selected by peer had a higher priority then anything we could try"));
+                                    "The candidate selected by peer had a higher priority then"
+                                            + " anything we could try"));
                     return;
                 }
                 try {
@@ -864,7 +862,7 @@ public class SocksByteStreamsTransport implements Transport {
             return new Candidate(
                     cid,
                     host,
-                    Jid.ofEscaped(jid),
+                    Jid.of(jid),
                     Integer.parseInt(port),
                     Integer.parseInt(priority),
                     CandidateType.valueOf(type.toUpperCase(Locale.ROOT)));
diff --git a/src/main/java/im/conversations/android/xmpp/model/bind/Bind.java b/src/main/java/im/conversations/android/xmpp/model/bind/Bind.java
index 27264f754..6a7c8605c 100644
--- a/src/main/java/im/conversations/android/xmpp/model/bind/Bind.java
+++ b/src/main/java/im/conversations/android/xmpp/model/bind/Bind.java
@@ -1,7 +1,6 @@
 package im.conversations.android.xmpp.model.bind;
 
 import com.google.common.base.Strings;
-
 import im.conversations.android.annotation.XmlElement;
 import im.conversations.android.xmpp.model.Extension;
 
@@ -26,7 +25,7 @@ public class Bind extends Extension {
             return null;
         }
         try {
-            return eu.siacs.conversations.xmpp.Jid.ofEscaped(content);
+            return eu.siacs.conversations.xmpp.Jid.of(content);
         } catch (final IllegalArgumentException e) {
             return null;
         }
diff --git a/src/main/java/im/conversations/android/xmpp/model/sasl2/AuthorizationIdentifier.java b/src/main/java/im/conversations/android/xmpp/model/sasl2/AuthorizationIdentifier.java
index e29ae7dea..d477131b8 100644
--- a/src/main/java/im/conversations/android/xmpp/model/sasl2/AuthorizationIdentifier.java
+++ b/src/main/java/im/conversations/android/xmpp/model/sasl2/AuthorizationIdentifier.java
@@ -1,7 +1,6 @@
 package im.conversations.android.xmpp.model.sasl2;
 
 import com.google.common.base.Strings;
-
 import eu.siacs.conversations.xmpp.Jid;
 import im.conversations.android.annotation.XmlElement;
 import im.conversations.android.xmpp.model.Extension;
@@ -9,18 +8,17 @@ import im.conversations.android.xmpp.model.Extension;
 @XmlElement
 public class AuthorizationIdentifier extends Extension {
 
-
     public AuthorizationIdentifier() {
         super(AuthorizationIdentifier.class);
     }
 
     public Jid get() {
         final var content = getContent();
-        if ( Strings.isNullOrEmpty(content)) {
+        if (Strings.isNullOrEmpty(content)) {
             return null;
         }
         try {
-            return Jid.ofEscaped(content);
+            return Jid.of(content);
         } catch (final IllegalArgumentException e) {
             return null;
         }
diff --git a/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java b/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java
index 82a8ce3df..3abf016ff 100644
--- a/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java
+++ b/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java
@@ -1,10 +1,7 @@
 package im.conversations.android.xmpp.model.stanza;
 
 import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.xmpp.InvalidJid;
 import eu.siacs.conversations.xmpp.Jid;
-
-import im.conversations.android.xmpp.model.Extension;
 import im.conversations.android.xmpp.model.StreamElement;
 import im.conversations.android.xmpp.model.error.Error;
 
@@ -45,7 +42,7 @@ public abstract class Stanza extends StreamElement {
     public boolean isInvalid() {
         final var to = getTo();
         final var from = getFrom();
-        if (to instanceof InvalidJid || from instanceof InvalidJid) {
+        if (to instanceof Jid.Invalid || from instanceof Jid.Invalid) {
             return true;
         }
         return false;
-- 
GitLab