get rid of jid escaping api

Daniel Gultsch created

Conversations never supported Jid Escaping but the underlaying library
did so we had API for that which could lead to confusion

Change summary

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

Detailed changes

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,
     }
-
 }
-

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(

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);
     }
 

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);

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();

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);

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);
     }
-
 }

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;
     }
-}
+}

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);
     }
 }

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, '&');
         }
     }
 

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();
+    }
 }

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

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)),

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() {

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());
     }
-}
+}

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");
         }

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();
     }
-}
+}

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;
     }
 }

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;
+    }
 }

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> {
             }
         }
     }
-
 }

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 =

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())) {

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);

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;

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();
+    }
 }

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() {

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");
             }

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);
                     }

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(

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) {

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);
         }

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);
                     }

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();
+    }
 }

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();
+    }
 }

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(

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;
             }
         }

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);

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

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);

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;

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);
+        }
+    }
 }

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();
         }
     }

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());

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);

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);
+                });
     }
 }

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);
                     }

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(

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;
         }

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));

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));
+    }
 }

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) == '}';
     }
-}
+}

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;
         }

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);
     }
-
 }

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) {

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));

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;

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 {}
 }

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");
     }
 }

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);
+        }
+    }
 }

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());
     }
-
 }

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 {

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);

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() {

src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java 🔗

@@ -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;
-		}
-	}
-}

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;
+            }
+        }
+    }
 }

src/main/java/eu/siacs/conversations/xmpp/WrappedJid.java 🔗

@@ -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();
-    }
-}

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;
     }
 

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());

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);

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)));

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;
         }

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;
         }

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;