add mime type and icon for pcap files

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/crypto/PgpEngine.java                    | 352 
src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java       |   8 
src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java   |  80 
src/main/java/eu/siacs/conversations/entities/Edit.java                       |  14 
src/main/java/eu/siacs/conversations/entities/MucOptions.java                 |   6 
src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java           | 143 
src/main/java/eu/siacs/conversations/entities/ReadByMarker.java               | 320 
src/main/java/eu/siacs/conversations/http/SlotRequester.java                  |   2 
src/main/java/eu/siacs/conversations/parser/IqParser.java                     |   4 
src/main/java/eu/siacs/conversations/parser/MessageParser.java                |   1 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java         |   4 
src/main/java/eu/siacs/conversations/persistance/FileBackend.java             |   8 
src/main/java/eu/siacs/conversations/services/CallIntegration.java            |   5 
src/main/java/eu/siacs/conversations/services/MessageArchiveService.java      | 236 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java      |   6 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java             |   3 
src/main/java/eu/siacs/conversations/ui/CreatePrivateGroupChatDialog.java     |  44 
src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java        |   2 
src/main/java/eu/siacs/conversations/ui/JoinConferenceDialog.java             | 218 
src/main/java/eu/siacs/conversations/ui/ShortcutActivity.java                 |  54 
src/main/java/eu/siacs/conversations/ui/XmppActivity.java                     |   3 
src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java        |   2 
src/main/java/eu/siacs/conversations/ui/adapter/MediaAdapter.java             |  11 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java           |   2 
src/main/java/eu/siacs/conversations/ui/service/CameraManager.java            | 144 
src/main/java/eu/siacs/conversations/ui/util/AvatarWorkerTask.java            |  42 
src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java | 133 
src/main/java/eu/siacs/conversations/ui/widget/ScannerView.java               |  24 
src/main/java/eu/siacs/conversations/utils/CursorUtils.java                   |   4 
src/main/java/eu/siacs/conversations/utils/GeoHelper.java                     |   9 
src/main/java/eu/siacs/conversations/utils/ImStyleParser.java                 |  28 
src/main/java/eu/siacs/conversations/utils/MimeUtils.java                     |   8 
src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java                     |   3 
src/main/java/im/conversations/android/xmpp/model/muc/Affiliation.java        |   2 
src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java          |   5 
src/main/res/drawable/ic_help_center_48dp.xml                                 |  16 
src/main/res/drawable/ic_lan_24dp.xml                                         |  12 
37 files changed, 1,105 insertions(+), 853 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/PgpEngine.java 🔗

@@ -3,27 +3,10 @@ package eu.siacs.conversations.crypto;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.util.Log;
-
 import androidx.annotation.StringRes;
-
 import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
-
-import org.openintents.openpgp.OpenPgpError;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.openintents.openpgp.util.OpenPgpApi;
-import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
@@ -35,6 +18,18 @@ import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.ui.UiCallback;
 import eu.siacs.conversations.utils.AsciiArmor;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import org.openintents.openpgp.OpenPgpError;
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.openintents.openpgp.util.OpenPgpApi.IOpenPgpCallback;
 
 public class PgpEngine {
     private final OpenPgpApi api;
@@ -48,9 +43,20 @@ public class PgpEngine {
     private static void logError(Account account, OpenPgpError error) {
         if (error != null) {
             error.describeContents();
-            Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": OpenKeychain error '" + error.getMessage() + "' code=" + error.getErrorId() + " class=" + error.getClass().getName());
+            Log.d(
+                    Config.LOGTAG,
+                    account.getJid().asBareJid().toString()
+                            + ": OpenKeychain error '"
+                            + error.getMessage()
+                            + "' code="
+                            + error.getErrorId()
+                            + " class="
+                            + error.getClass().getName());
         } else {
-            Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": OpenKeychain error with no message");
+            Log.d(
+                    Config.LOGTAG,
+                    account.getJid().asBareJid().toString()
+                            + ": OpenKeychain error with no message");
         }
     }
 
@@ -60,8 +66,7 @@ public class PgpEngine {
         final Conversation conversation = (Conversation) message.getConversation();
         if (conversation.getMode() == Conversation.MODE_SINGLE) {
             long[] keys = {
-                    conversation.getContact().getPgpKeyId(),
-                    conversation.getAccount().getPgpId()
+                conversation.getContact().getPgpKeyId(), conversation.getAccount().getPgpId()
             };
             params.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keys);
         } else {
@@ -78,75 +83,95 @@ public class PgpEngine {
             }
             InputStream is = new ByteArrayInputStream(body.getBytes());
             final OutputStream os = new ByteArrayOutputStream();
-            api.executeApiAsync(params, is, os, result -> {
-                switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
-                    case OpenPgpApi.RESULT_CODE_SUCCESS:
-                        try {
-                            os.flush();
-                            final ArrayList<String> encryptedMessageBody = new ArrayList<>();
-                            final String[] lines = os.toString().split("\n");
-                            for (int i = 2; i < lines.length - 1; ++i) {
-                                if (!lines[i].contains("Version")) {
-                                    encryptedMessageBody.add(lines[i].trim());
+            api.executeApiAsync(
+                    params,
+                    is,
+                    os,
+                    result -> {
+                        switch (result.getIntExtra(
+                                OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
+                            case OpenPgpApi.RESULT_CODE_SUCCESS:
+                                try {
+                                    os.flush();
+                                    final ArrayList<String> encryptedMessageBody =
+                                            new ArrayList<>();
+                                    final String[] lines = os.toString().split("\n");
+                                    for (int i = 2; i < lines.length - 1; ++i) {
+                                        if (!lines[i].contains("Version")) {
+                                            encryptedMessageBody.add(lines[i].trim());
+                                        }
+                                    }
+                                    message.setEncryptedBody(
+                                            Joiner.on('\n').join(encryptedMessageBody));
+                                    message.setEncryption(Message.ENCRYPTION_DECRYPTED);
+                                    mXmppConnectionService.sendMessage(message);
+                                    callback.success(message);
+                                } catch (IOException e) {
+                                    callback.error(R.string.openpgp_error, message);
                                 }
-                            }
-                            message.setEncryptedBody(Joiner.on('\n').join(encryptedMessageBody));
-                            message.setEncryption(Message.ENCRYPTION_DECRYPTED);
-                            mXmppConnectionService.sendMessage(message);
-                            callback.success(message);
-                        } catch (IOException e) {
-                            callback.error(R.string.openpgp_error, message);
-                        }
 
-                        break;
-                    case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
-                        callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), message);
-                        break;
-                    case OpenPgpApi.RESULT_CODE_ERROR:
-                        OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
-                        String errorMessage = error != null ? error.getMessage() : null;
-                        @StringRes final int res;
-                        if (errorMessage != null && errorMessage.startsWith("Bad key for encryption")) {
-                            res = R.string.bad_key_for_encryption;
-                        } else {
-                            res = R.string.openpgp_error;
+                                break;
+                            case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+                                callback.userInputRequired(
+                                        result.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+                                        message);
+                                break;
+                            case OpenPgpApi.RESULT_CODE_ERROR:
+                                OpenPgpError error =
+                                        result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+                                String errorMessage = error != null ? error.getMessage() : null;
+                                @StringRes final int res;
+                                if (errorMessage != null
+                                        && errorMessage.startsWith("Bad key for encryption")) {
+                                    res = R.string.bad_key_for_encryption;
+                                } else {
+                                    res = R.string.openpgp_error;
+                                }
+                                logError(conversation.getAccount(), error);
+                                callback.error(res, message);
+                                break;
                         }
-                        logError(conversation.getAccount(), error);
-                        callback.error(res, message);
-                        break;
-                }
-            });
+                    });
         } else {
             try {
-                DownloadableFile inputFile = this.mXmppConnectionService
-                        .getFileBackend().getFile(message, true);
-                DownloadableFile outputFile = this.mXmppConnectionService
-                        .getFileBackend().getFile(message, false);
+                DownloadableFile inputFile =
+                        this.mXmppConnectionService.getFileBackend().getFile(message, true);
+                DownloadableFile outputFile =
+                        this.mXmppConnectionService.getFileBackend().getFile(message, false);
                 outputFile.getParentFile().mkdirs();
                 outputFile.createNewFile();
                 final InputStream is = new FileInputStream(inputFile);
                 final OutputStream os = new FileOutputStream(outputFile);
-                api.executeApiAsync(params, is, os, result -> {
-                    switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
-                        case OpenPgpApi.RESULT_CODE_SUCCESS:
-                            try {
-                                os.flush();
-                            } catch (IOException ignored) {
-                                //ignored
+                api.executeApiAsync(
+                        params,
+                        is,
+                        os,
+                        result -> {
+                            switch (result.getIntExtra(
+                                    OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
+                                case OpenPgpApi.RESULT_CODE_SUCCESS:
+                                    try {
+                                        os.flush();
+                                    } catch (IOException ignored) {
+                                        // ignored
+                                    }
+                                    FileBackend.close(os);
+                                    mXmppConnectionService.sendMessage(message);
+                                    callback.success(message);
+                                    break;
+                                case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+                                    callback.userInputRequired(
+                                            result.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+                                            message);
+                                    break;
+                                case OpenPgpApi.RESULT_CODE_ERROR:
+                                    logError(
+                                            conversation.getAccount(),
+                                            result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+                                    callback.error(R.string.openpgp_error, message);
+                                    break;
                             }
-                            FileBackend.close(os);
-                            mXmppConnectionService.sendMessage(message);
-                            callback.success(message);
-                            break;
-                        case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
-                            callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), message);
-                            break;
-                        case OpenPgpApi.RESULT_CODE_ERROR:
-                            logError(conversation.getAccount(), result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
-                            callback.error(R.string.openpgp_error, message);
-                            break;
-                    }
-                });
+                        });
             } catch (final IOException e) {
                 callback.error(R.string.openpgp_error, message);
             }
@@ -168,11 +193,11 @@ public class PgpEngine {
         final InputStream is = new ByteArrayInputStream(Strings.nullToEmpty(status).getBytes());
         final ByteArrayOutputStream os = new ByteArrayOutputStream();
         final Intent result = api.executeApi(params, is, os);
-        switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
-                OpenPgpApi.RESULT_CODE_ERROR)) {
+        switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
             case OpenPgpApi.RESULT_CODE_SUCCESS:
-                final OpenPgpSignatureResult sigResult = result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
-                //TODO unsure that sigResult.getResult() is either 1, 2 or 3
+                final OpenPgpSignatureResult sigResult =
+                        result.getParcelableExtra(OpenPgpApi.RESULT_SIGNATURE);
+                // TODO unsure that sigResult.getResult() is either 1, 2 or 3
                 if (sigResult != null) {
                     return sigResult.getKeyId();
                 } else {
@@ -190,22 +215,31 @@ public class PgpEngine {
     public void chooseKey(final Account account, final UiCallback<Account> callback) {
         Intent p = new Intent();
         p.setAction(OpenPgpApi.ACTION_GET_SIGN_KEY_ID);
-        api.executeApiAsync(p, null, null, result -> {
-            switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
-                case OpenPgpApi.RESULT_CODE_SUCCESS:
-                    callback.success(account);
-                    return;
-                case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
-                    callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), account);
-                    return;
-                case OpenPgpApi.RESULT_CODE_ERROR:
-                    logError(account, result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
-                    callback.error(R.string.openpgp_error, account);
-            }
-        });
+        api.executeApiAsync(
+                p,
+                null,
+                null,
+                result -> {
+                    switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+                        case OpenPgpApi.RESULT_CODE_SUCCESS:
+                            callback.success(account);
+                            return;
+                        case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+                            callback.userInputRequired(
+                                    result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), account);
+                            return;
+                        case OpenPgpApi.RESULT_CODE_ERROR:
+                            logError(account, result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+                            callback.error(R.string.openpgp_error, account);
+                    }
+                });
     }
 
-    public void generateSignature(Intent intent, final Account account, String status, final UiCallback<String> callback) {
+    public void generateSignature(
+            Intent intent,
+            final Account account,
+            String status,
+            final UiCallback<String> callback) {
         if (account.getPgpId() == 0) {
             return;
         }
@@ -215,70 +249,86 @@ public class PgpEngine {
         params.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, account.getPgpId());
         InputStream is = new ByteArrayInputStream(status.getBytes());
         final OutputStream os = new ByteArrayOutputStream();
-        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": signing status message \"" + status + "\"");
-        api.executeApiAsync(params, is, os, result -> {
-            switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
-                case OpenPgpApi.RESULT_CODE_SUCCESS:
-                    final ArrayList<String> signature = new ArrayList<>();
-                    try {
-                        os.flush();
-                        boolean sig = false;
-                        for (final String line : Splitter.on('\n').split(os.toString())) {
-                            if (sig) {
-                                if (line.contains("END PGP SIGNATURE")) {
-                                    sig = false;
-                                } else {
-                                    if (!line.contains("Version")) {
-                                        signature.add(line.trim());
+        Log.d(
+                Config.LOGTAG,
+                account.getJid().asBareJid() + ": signing status message \"" + status + "\"");
+        api.executeApiAsync(
+                params,
+                is,
+                os,
+                result -> {
+                    switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+                        case OpenPgpApi.RESULT_CODE_SUCCESS:
+                            final ArrayList<String> signature = new ArrayList<>();
+                            try {
+                                os.flush();
+                                boolean sig = false;
+                                for (final String line : Splitter.on('\n').split(os.toString())) {
+                                    if (sig) {
+                                        if (line.contains("END PGP SIGNATURE")) {
+                                            sig = false;
+                                        } else {
+                                            if (!line.contains("Version")) {
+                                                signature.add(line.trim());
+                                            }
+                                        }
+                                    }
+                                    if (line.contains("BEGIN PGP SIGNATURE")) {
+                                        sig = true;
                                     }
                                 }
+                            } catch (IOException e) {
+                                callback.error(R.string.openpgp_error, null);
+                                return;
                             }
-                            if (line.contains("BEGIN PGP SIGNATURE")) {
-                                sig = true;
+                            callback.success(Joiner.on('\n').join(signature));
+                            return;
+                        case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+                            callback.userInputRequired(
+                                    result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), status);
+                            return;
+                        case OpenPgpApi.RESULT_CODE_ERROR:
+                            OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
+                            if (error != null
+                                    && "signing subkey not found!".equals(error.getMessage())) {
+                                callback.error(0, null);
+                            } else {
+                                logError(account, error);
+                                callback.error(R.string.unable_to_connect_to_keychain, null);
                             }
-                        }
-                    } catch (IOException e) {
-                        callback.error(R.string.openpgp_error, null);
-                        return;
                     }
-                    callback.success(Joiner.on('\n').join(signature));
-                    return;
-                case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
-                    callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), status);
-                    return;
-                case OpenPgpApi.RESULT_CODE_ERROR:
-                    OpenPgpError error = result.getParcelableExtra(OpenPgpApi.RESULT_ERROR);
-                    if (error != null && "signing subkey not found!".equals(error.getMessage())) {
-                        callback.error(0, null);
-                    } else {
-                        logError(account, error);
-                        callback.error(R.string.unable_to_connect_to_keychain, null);
-                    }
-            }
-        });
+                });
     }
 
     public void hasKey(final Contact contact, final UiCallback<Contact> callback) {
         Intent params = new Intent();
         params.setAction(OpenPgpApi.ACTION_GET_KEY);
         params.putExtra(OpenPgpApi.EXTRA_KEY_ID, contact.getPgpKeyId());
-        api.executeApiAsync(params, null, null, new IOpenPgpCallback() {
+        api.executeApiAsync(
+                params,
+                null,
+                null,
+                new IOpenPgpCallback() {
 
-            @Override
-            public void onReturn(Intent result) {
-                switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
-                    case OpenPgpApi.RESULT_CODE_SUCCESS:
-                        callback.success(contact);
-                        return;
-                    case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
-                        callback.userInputRequired(result.getParcelableExtra(OpenPgpApi.RESULT_INTENT), contact);
-                        return;
-                    case OpenPgpApi.RESULT_CODE_ERROR:
-                        logError(contact.getAccount(), result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
-                        callback.error(R.string.openpgp_error, contact);
-                }
-            }
-        });
+                    @Override
+                    public void onReturn(Intent result) {
+                        switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0)) {
+                            case OpenPgpApi.RESULT_CODE_SUCCESS:
+                                callback.success(contact);
+                                return;
+                            case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
+                                callback.userInputRequired(
+                                        result.getParcelableExtra(OpenPgpApi.RESULT_INTENT),
+                                        contact);
+                                return;
+                            case OpenPgpApi.RESULT_CODE_ERROR:
+                                logError(
+                                        contact.getAccount(),
+                                        result.getParcelableExtra(OpenPgpApi.RESULT_ERROR));
+                                callback.error(R.string.openpgp_error, contact);
+                        }
+                    }
+                });
     }
 
     public PendingIntent getIntentForKey(long pgpKeyId) {
@@ -288,6 +338,6 @@ public class PgpEngine {
         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[0]);
         Intent result = api.executeApi(params, inputStream, outputStream);
-        return (PendingIntent) result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
+        return result.getParcelableExtra(OpenPgpApi.RESULT_INTENT);
     }
 }

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -951,7 +951,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
                                     Config.LOGTAG,
                                     getLogprefix(account)
                                             + "Error received while publishing bundle: "
-                                            + response.toString());
+                                            + response);
                         }
                         pepBroken = true;
                     }
@@ -1416,7 +1416,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
                                     Config.LOGTAG,
                                     AxolotlService.getLogprefix(account)
                                             + "Already have session for "
-                                            + address.toString()
+                                            + address
                                             + ", adding to cache...");
                             XmppAxolotlSession session =
                                     new XmppAxolotlSession(
@@ -1462,7 +1462,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
                             Config.LOGTAG,
                             AxolotlService.getLogprefix(account)
                                     + "Already have session for "
-                                    + address.toString()
+                                    + address
                                     + ", adding to cache...");
                     XmppAxolotlSession session =
                             new XmppAxolotlSession(account, axolotlStore, address, identityKey);
@@ -1538,7 +1538,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
                         Config.LOGTAG,
                         AxolotlService.getLogprefix(account)
                                 + "Already fetching bundle for "
-                                + address.toString());
+                                + address);
             }
         }
 

src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java 🔗

@@ -2,7 +2,10 @@ package eu.siacs.conversations.crypto.axolotl;
 
 import android.util.Base64;
 import android.util.Log;
-
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.utils.Compatibility;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.Jid;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -10,7 +13,6 @@ import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.List;
-
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
@@ -20,11 +22,6 @@ import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.utils.Compatibility;
-import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.Jid;
-
 public class XmppAxolotlMessage {
     public static final String CONTAINERTAG = "encrypted";
     private static final String HEADER = "header";
@@ -45,7 +42,8 @@ public class XmppAxolotlMessage {
     private byte[] authtagPlusInnerKey = null;
     private byte[] iv = null;
 
-    private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
+    private XmppAxolotlMessage(final Element axolotlMessage, final Jid from)
+            throws IllegalArgumentException {
         this.from = from;
         Element header = axolotlMessage.findChild(HEADER);
         try {
@@ -62,7 +60,8 @@ public class XmppAxolotlMessage {
                         int recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
                         byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
                         boolean isPreKey = keyElement.getAttributeAsBoolean("prekey");
-                        this.keys.add(new XmppAxolotlSession.AxolotlKey(recipientId, key, isPreKey));
+                        this.keys.add(
+                                new XmppAxolotlSession.AxolotlKey(recipientId, key, isPreKey));
                     } catch (NumberFormatException e) {
                         throw new IllegalArgumentException("invalid remote id");
                     }
@@ -74,11 +73,12 @@ public class XmppAxolotlMessage {
                     iv = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
                     break;
                 default:
-                    Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement.toString());
+                    Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement);
                     break;
             }
         }
-        final Element payloadElement = axolotlMessage.findChildEnsureSingle(PAYLOAD, AxolotlService.PEP_PREFIX);
+        final Element payloadElement =
+                axolotlMessage.findChildEnsureSingle(PAYLOAD, AxolotlService.PEP_PREFIX);
         if (payloadElement != null) {
             ciphertext = Base64.decode(payloadElement.getContent().trim(), Base64.DEFAULT);
         }
@@ -149,9 +149,16 @@ public class XmppAxolotlMessage {
         try {
             SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
             IvParameterSpec ivSpec = new IvParameterSpec(iv);
-            Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+            Cipher cipher =
+                    Compatibility.twentyEight()
+                            ? Cipher.getInstance(CIPHERMODE)
+                            : Cipher.getInstance(CIPHERMODE, PROVIDER);
             cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
-            this.ciphertext = cipher.doFinal(Config.OMEMO_PADDING ? getPaddedBytes(plaintext) : plaintext.getBytes());
+            this.ciphertext =
+                    cipher.doFinal(
+                            Config.OMEMO_PADDING
+                                    ? getPaddedBytes(plaintext)
+                                    : plaintext.getBytes());
             if (Config.PUT_AUTH_TAG_INTO_KEY && this.ciphertext != null) {
                 this.authtagPlusInnerKey = new byte[16 + 16];
                 byte[] ciphertext = new byte[this.ciphertext.length - 16];
@@ -160,8 +167,12 @@ public class XmppAxolotlMessage {
                 System.arraycopy(this.innerKey, 0, authtagPlusInnerKey, 0, this.innerKey.length);
                 this.ciphertext = ciphertext;
             }
-        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
-                | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
+        } catch (NoSuchAlgorithmException
+                | NoSuchPaddingException
+                | InvalidKeyException
+                | IllegalBlockSizeException
+                | BadPaddingException
+                | NoSuchProviderException
                 | InvalidAlgorithmParameterException e) {
             throw new CryptoFailedException(e);
         }
@@ -220,7 +231,8 @@ public class XmppAxolotlMessage {
         return encryptionElement;
     }
 
-    private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
+    private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId)
+            throws CryptoFailedException {
         ArrayList<XmppAxolotlSession.AxolotlKey> possibleKeys = new ArrayList<>();
         for (XmppAxolotlSession.AxolotlKey key : keys) {
             if (key.deviceId == sourceDeviceId) {
@@ -233,17 +245,22 @@ public class XmppAxolotlMessage {
         return session.processReceiving(possibleKeys);
     }
 
-    XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
-        return new XmppAxolotlKeyTransportMessage(session.getFingerprint(), unpackKey(session, sourceDeviceId), getIV());
+    XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId)
+            throws CryptoFailedException {
+        return new XmppAxolotlKeyTransportMessage(
+                session.getFingerprint(), unpackKey(session, sourceDeviceId), getIV());
     }
 
-    public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException {
+    public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId)
+            throws CryptoFailedException {
         XmppAxolotlPlaintextMessage plaintextMessage = null;
         byte[] key = unpackKey(session, sourceDeviceId);
         if (key != null) {
             try {
                 if (key.length < 32) {
-                    throw new OutdatedSenderException("Key did not contain auth tag. Sender needs to update their OMEMO client");
+                    throw new OutdatedSenderException(
+                            "Key did not contain auth tag. Sender needs to update their OMEMO"
+                                    + " client");
                 }
                 final int authTagLength = key.length - 16;
                 byte[] newCipherText = new byte[key.length - 16 + ciphertext.length];
@@ -254,18 +271,28 @@ public class XmppAxolotlMessage {
                 ciphertext = newCipherText;
                 key = newKey;
 
-                final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+                final Cipher cipher =
+                        Compatibility.twentyEight()
+                                ? Cipher.getInstance(CIPHERMODE)
+                                : Cipher.getInstance(CIPHERMODE, PROVIDER);
                 SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
                 IvParameterSpec ivSpec = new IvParameterSpec(iv);
 
                 cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
 
                 String plaintext = new String(cipher.doFinal(ciphertext));
-                plaintextMessage = new XmppAxolotlPlaintextMessage(Config.OMEMO_PADDING ? plaintext.trim() : plaintext, session.getFingerprint());
-
-            } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
-                    | InvalidAlgorithmParameterException | IllegalBlockSizeException
-                    | BadPaddingException | NoSuchProviderException e) {
+                plaintextMessage =
+                        new XmppAxolotlPlaintextMessage(
+                                Config.OMEMO_PADDING ? plaintext.trim() : plaintext,
+                                session.getFingerprint());
+
+            } catch (NoSuchAlgorithmException
+                    | NoSuchPaddingException
+                    | InvalidKeyException
+                    | InvalidAlgorithmParameterException
+                    | IllegalBlockSizeException
+                    | BadPaddingException
+                    | NoSuchProviderException e) {
                 throw new CryptoFailedException(e);
             }
         }
@@ -285,7 +312,6 @@ public class XmppAxolotlMessage {
             return plaintext;
         }
 
-
         public String getFingerprint() {
             return fingerprint;
         }

src/main/java/eu/siacs/conversations/entities/Edit.java 🔗

@@ -1,12 +1,12 @@
 package eu.siacs.conversations.entities;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import java.util.ArrayList;
-import java.util.List;
-
 public class Edit {
 
     private final String editedId;
@@ -45,7 +45,8 @@ public class Edit {
 
     private static Edit fromJson(JSONObject jsonObject) throws JSONException {
         String edited = jsonObject.has("edited_id") ? jsonObject.getString("edited_id") : null;
-        String serverMsgId = jsonObject.has("server_msg_id") ? jsonObject.getString("server_msg_id") : null;
+        String serverMsgId =
+                jsonObject.has("server_msg_id") ? jsonObject.getString("server_msg_id") : null;
         return new Edit(edited, serverMsgId);
     }
 
@@ -83,9 +84,8 @@ public class Edit {
 
         Edit edit = (Edit) o;
 
-        if (editedId != null ? !editedId.equals(edit.editedId) : edit.editedId != null)
-            return false;
-        return serverMsgId != null ? serverMsgId.equals(edit.serverMsgId) : edit.serverMsgId == null;
+        if (!Objects.equals(editedId, edit.editedId)) return false;
+        return Objects.equals(serverMsgId, edit.serverMsgId);
     }
 
     @Override

src/main/java/eu/siacs/conversations/entities/MucOptions.java 🔗

@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 public class MucOptions {
@@ -919,9 +920,8 @@ public class MucOptions {
 
             if (role != user.role) return false;
             if (affiliation != user.affiliation) return false;
-            if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null)
-                return false;
-            return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null;
+            if (!Objects.equals(realJid, user.realJid)) return false;
+            return Objects.equals(fullJid, user.fullJid);
         }
 
         public boolean isDomain() {

src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java 🔗

@@ -2,80 +2,77 @@ package eu.siacs.conversations.entities;
 
 import android.content.ContentValues;
 import android.database.Cursor;
-
+import java.util.Objects;
 
 public class PresenceTemplate extends AbstractEntity {
 
-	public static final String TABELNAME = "presence_templates";
-	public static final String LAST_USED = "last_used";
-	public static final String MESSAGE = "message";
-	public static final String STATUS = "status";
-
-	private long lastUsed = 0;
-	private String statusMessage;
-	private Presence.Status status = Presence.Status.ONLINE;
-
-	public PresenceTemplate(Presence.Status status, String statusMessage) {
-		this.status = status;
-		this.statusMessage = statusMessage;
-		this.lastUsed = System.currentTimeMillis();
-		this.uuid = java.util.UUID.randomUUID().toString();
-	}
-
-	private PresenceTemplate() {
-
-	}
-
-	@Override
-	public ContentValues getContentValues() {
-		final String show = status.toShowString();
-		ContentValues values = new ContentValues();
-		values.put(LAST_USED, lastUsed);
-		values.put(MESSAGE, statusMessage);
-		values.put(STATUS, show == null ? "" : show);
-		values.put(UUID, uuid);
-		return values;
-	}
-
-	public static PresenceTemplate fromCursor(Cursor cursor) {
-		PresenceTemplate template = new PresenceTemplate();
-		template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
-		template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
-		template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
-		template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
-		return template;
-	}
-
-	public Presence.Status getStatus() {
-		return status;
-	}
-
-	public String getStatusMessage() {
-		return statusMessage;
-	}
-
-	@Override
-	public boolean equals(Object o) {
-		if (this == o) return true;
-		if (o == null || getClass() != o.getClass()) return false;
-
-		PresenceTemplate template = (PresenceTemplate) o;
-
-		if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null)
-			return false;
-		return status == template.status;
-
-	}
-
-	@Override
-	public int hashCode() {
-		int result = statusMessage != null ? statusMessage.hashCode() : 0;
-		result = 31 * result + status.hashCode();
-		return result;
-	}
-
-	@Override
-	public String toString() {
-		return statusMessage;
-	}
+    public static final String TABELNAME = "presence_templates";
+    public static final String LAST_USED = "last_used";
+    public static final String MESSAGE = "message";
+    public static final String STATUS = "status";
+
+    private long lastUsed = 0;
+    private String statusMessage;
+    private Presence.Status status = Presence.Status.ONLINE;
+
+    public PresenceTemplate(Presence.Status status, String statusMessage) {
+        this.status = status;
+        this.statusMessage = statusMessage;
+        this.lastUsed = System.currentTimeMillis();
+        this.uuid = java.util.UUID.randomUUID().toString();
+    }
+
+    private PresenceTemplate() {}
+
+    @Override
+    public ContentValues getContentValues() {
+        final String show = status.toShowString();
+        ContentValues values = new ContentValues();
+        values.put(LAST_USED, lastUsed);
+        values.put(MESSAGE, statusMessage);
+        values.put(STATUS, show == null ? "" : show);
+        values.put(UUID, uuid);
+        return values;
+    }
+
+    public static PresenceTemplate fromCursor(Cursor cursor) {
+        PresenceTemplate template = new PresenceTemplate();
+        template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
+        template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
+        template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
+        template.status =
+                Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+        return template;
+    }
+
+    public Presence.Status getStatus() {
+        return status;
+    }
+
+    public String getStatusMessage() {
+        return statusMessage;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        PresenceTemplate template = (PresenceTemplate) o;
+
+        if (!Objects.equals(statusMessage, template.statusMessage)) return false;
+        return status == template.status;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = statusMessage != null ? statusMessage.hashCode() : 0;
+        result = 31 * result + status.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return statusMessage;
+    }
 }

src/main/java/eu/siacs/conversations/entities/ReadByMarker.java 🔗

@@ -1,171 +1,167 @@
 package eu.siacs.conversations.entities;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
+import eu.siacs.conversations.xmpp.Jid;
 import java.util.Collection;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
-
-import eu.siacs.conversations.xmpp.Jid;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
 
 public class ReadByMarker {
 
-	private ReadByMarker() {
-
-	}
-
-	@Override
-	public boolean equals(Object o) {
-		if (this == o) return true;
-		if (o == null || getClass() != o.getClass()) return false;
-
-		ReadByMarker marker = (ReadByMarker) o;
-
-		if (fullJid != null ? !fullJid.equals(marker.fullJid) : marker.fullJid != null)
-			return false;
-		return realJid != null ? realJid.equals(marker.realJid) : marker.realJid == null;
-
-	}
-
-	@Override
-	public int hashCode() {
-		int result = fullJid != null ? fullJid.hashCode() : 0;
-		result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
-		return result;
-	}
-
-	private Jid fullJid;
-	private Jid realJid;
-
-	public Jid getFullJid() {
-		return fullJid;
-	}
-
-	public Jid getRealJid() {
-		return realJid;
-	}
-
-	public JSONObject toJson() {
-		JSONObject jsonObject = new JSONObject();
-		if (fullJid != null) {
-			try {
-				jsonObject.put("fullJid", fullJid.toString());
-			} catch (JSONException e) {
-				//ignore
-			}
-		}
-		if (realJid != null) {
-			try {
-				jsonObject.put("realJid", realJid.toString());
-			} catch (JSONException e) {
-				//ignore
-			}
-		}
-		return jsonObject;
-	}
-
-	public static Set<ReadByMarker> fromJson(final JSONArray jsonArray) {
-		final Set<ReadByMarker> readByMarkers = new CopyOnWriteArraySet<>();
-		for(int i = 0; i < jsonArray.length(); ++i) {
-			try {
-				readByMarkers.add(fromJson(jsonArray.getJSONObject(i)));
-			} catch (JSONException e) {
-				//ignored
-			}
-		}
-		return readByMarkers;
-	}
-
-	public static ReadByMarker from(Jid fullJid, Jid realJid) {
-		final ReadByMarker marker = new ReadByMarker();
-		marker.fullJid = fullJid;
-		marker.realJid = realJid == null ? null : realJid.asBareJid();
-		return marker;
-	}
-
-	public static ReadByMarker from(Message message) {
-		final ReadByMarker marker = new ReadByMarker();
-		marker.fullJid = message.getCounterpart();
-		marker.realJid = message.getTrueCounterpart();
-		return marker;
-	}
-
-	public static ReadByMarker from(MucOptions.User user) {
-		final ReadByMarker marker = new ReadByMarker();
-		marker.fullJid = user.getFullJid();
-		marker.realJid = user.getRealJid();
-		return marker;
-	}
-
-	public static Set<ReadByMarker> from(Collection<MucOptions.User> users) {
-		final Set<ReadByMarker> markers = new CopyOnWriteArraySet<>();
-		for(MucOptions.User user : users) {
-			markers.add(from(user));
-		}
-		return markers;
-	}
-
-	public static ReadByMarker fromJson(JSONObject jsonObject) {
-		ReadByMarker marker = new ReadByMarker();
-		try {
-			marker.fullJid = Jid.of(jsonObject.getString("fullJid"));
-		} catch (JSONException | IllegalArgumentException e) {
-			marker.fullJid = null;
-		}
-		try {
-			marker.realJid = Jid.of(jsonObject.getString("realJid"));
-		} catch (JSONException | IllegalArgumentException e) {
-			marker.realJid = null;
-		}
-		return marker;
-	}
-
-	public static Set<ReadByMarker> fromJsonString(String json) {
-		try {
-			return fromJson(new JSONArray(json));
-		} catch (final JSONException | NullPointerException e) {
-			return new CopyOnWriteArraySet<>();
-		}
-	}
-
-	public static JSONArray toJson(final Set<ReadByMarker> readByMarkers) {
-		final JSONArray jsonArray = new JSONArray();
-		for(final ReadByMarker marker : readByMarkers) {
-			jsonArray.put(marker.toJson());
-		}
-		return jsonArray;
-	}
-
-	public static boolean contains(ReadByMarker needle, final Set<ReadByMarker> readByMarkers) {
-		for(final ReadByMarker marker : readByMarkers) {
-			if (marker.realJid != null && needle.realJid != null) {
-				if (marker.realJid.asBareJid().equals(needle.realJid.asBareJid())) {
-					return true;
-				}
-			} else if (marker.fullJid != null && needle.fullJid != null) {
-				if (marker.fullJid.equals(needle.fullJid)) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-
-	public static boolean allUsersRepresented(Collection<MucOptions.User> users, Set<ReadByMarker> markers) {
-		for(MucOptions.User user : users) {
-			if (!contains(from(user),markers)) {
-				return false;
-			}
-		}
-		return true;
-	}
-
-	public static boolean allUsersRepresented(Collection<MucOptions.User> users, Set<ReadByMarker> markers, ReadByMarker marker) {
-		final Set<ReadByMarker> markersCopy = new CopyOnWriteArraySet<>(markers);
-		markersCopy.add(marker);
-		return allUsersRepresented(users, markersCopy);
-	}
-
+    private ReadByMarker() {}
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ReadByMarker marker = (ReadByMarker) o;
+
+        if (!Objects.equals(fullJid, marker.fullJid)) return false;
+        return Objects.equals(realJid, marker.realJid);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = fullJid != null ? fullJid.hashCode() : 0;
+        result = 31 * result + (realJid != null ? realJid.hashCode() : 0);
+        return result;
+    }
+
+    private Jid fullJid;
+    private Jid realJid;
+
+    public Jid getFullJid() {
+        return fullJid;
+    }
+
+    public Jid getRealJid() {
+        return realJid;
+    }
+
+    public JSONObject toJson() {
+        JSONObject jsonObject = new JSONObject();
+        if (fullJid != null) {
+            try {
+                jsonObject.put("fullJid", fullJid.toString());
+            } catch (JSONException e) {
+                // ignore
+            }
+        }
+        if (realJid != null) {
+            try {
+                jsonObject.put("realJid", realJid.toString());
+            } catch (JSONException e) {
+                // ignore
+            }
+        }
+        return jsonObject;
+    }
+
+    public static Set<ReadByMarker> fromJson(final JSONArray jsonArray) {
+        final Set<ReadByMarker> readByMarkers = new CopyOnWriteArraySet<>();
+        for (int i = 0; i < jsonArray.length(); ++i) {
+            try {
+                readByMarkers.add(fromJson(jsonArray.getJSONObject(i)));
+            } catch (JSONException e) {
+                // ignored
+            }
+        }
+        return readByMarkers;
+    }
+
+    public static ReadByMarker from(Jid fullJid, Jid realJid) {
+        final ReadByMarker marker = new ReadByMarker();
+        marker.fullJid = fullJid;
+        marker.realJid = realJid == null ? null : realJid.asBareJid();
+        return marker;
+    }
+
+    public static ReadByMarker from(Message message) {
+        final ReadByMarker marker = new ReadByMarker();
+        marker.fullJid = message.getCounterpart();
+        marker.realJid = message.getTrueCounterpart();
+        return marker;
+    }
+
+    public static ReadByMarker from(MucOptions.User user) {
+        final ReadByMarker marker = new ReadByMarker();
+        marker.fullJid = user.getFullJid();
+        marker.realJid = user.getRealJid();
+        return marker;
+    }
+
+    public static Set<ReadByMarker> from(Collection<MucOptions.User> users) {
+        final Set<ReadByMarker> markers = new CopyOnWriteArraySet<>();
+        for (MucOptions.User user : users) {
+            markers.add(from(user));
+        }
+        return markers;
+    }
+
+    public static ReadByMarker fromJson(JSONObject jsonObject) {
+        ReadByMarker marker = new ReadByMarker();
+        try {
+            marker.fullJid = Jid.of(jsonObject.getString("fullJid"));
+        } catch (JSONException | IllegalArgumentException e) {
+            marker.fullJid = null;
+        }
+        try {
+            marker.realJid = Jid.of(jsonObject.getString("realJid"));
+        } catch (JSONException | IllegalArgumentException e) {
+            marker.realJid = null;
+        }
+        return marker;
+    }
+
+    public static Set<ReadByMarker> fromJsonString(String json) {
+        try {
+            return fromJson(new JSONArray(json));
+        } catch (final JSONException | NullPointerException e) {
+            return new CopyOnWriteArraySet<>();
+        }
+    }
+
+    public static JSONArray toJson(final Set<ReadByMarker> readByMarkers) {
+        final JSONArray jsonArray = new JSONArray();
+        for (final ReadByMarker marker : readByMarkers) {
+            jsonArray.put(marker.toJson());
+        }
+        return jsonArray;
+    }
+
+    public static boolean contains(ReadByMarker needle, final Set<ReadByMarker> readByMarkers) {
+        for (final ReadByMarker marker : readByMarkers) {
+            if (marker.realJid != null && needle.realJid != null) {
+                if (marker.realJid.asBareJid().equals(needle.realJid.asBareJid())) {
+                    return true;
+                }
+            } else if (marker.fullJid != null && needle.fullJid != null) {
+                if (marker.fullJid.equals(needle.fullJid)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public static boolean allUsersRepresented(
+            Collection<MucOptions.User> users, Set<ReadByMarker> markers) {
+        for (MucOptions.User user : users) {
+            if (!contains(from(user), markers)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean allUsersRepresented(
+            Collection<MucOptions.User> users, Set<ReadByMarker> markers, ReadByMarker marker) {
+        final Set<ReadByMarker> markersCopy = new CopyOnWriteArraySet<>(markers);
+        markersCopy.add(marker);
+        return allUsersRepresented(users, markersCopy);
+    }
 }

src/main/java/eu/siacs/conversations/http/SlotRequester.java 🔗

@@ -79,7 +79,7 @@ public class SlotRequester {
                             response.getExtension(
                                     im.conversations.android.xmpp.model.upload.Slot.class);
                     if (slot == null) {
-                        Log.d(Config.LOGTAG, "-->" + response.toString());
+                        Log.d(Config.LOGTAG, "-->" + response);
                         throw new IllegalStateException("Slot not found in IQ response");
                     }
                     final var getUrl = slot.getGetUrl();

src/main/java/eu/siacs/conversations/parser/IqParser.java 🔗

@@ -189,7 +189,7 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                                         + "Encountered invalid <device> node in PEP ("
                                         + e.getMessage()
                                         + "):"
-                                        + device.toString()
+                                        + device
                                         + ", skipping...");
                     }
                 }
@@ -313,7 +313,7 @@ public class IqParser extends AbstractParser implements Consumer<Iq> {
                         AxolotlService.LOGPREFIX
                                 + " : "
                                 + "could not parse preKeyId from preKey "
-                                + preKeyPublicElement.toString());
+                                + preKeyPublicElement);
             } catch (Throwable e) {
                 Log.e(
                         Config.LOGTAG,

src/main/java/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -332,7 +332,6 @@ public class MessageParser extends AbstractParser
             }
         } else if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) {
             final var retractions = items.getRetractions();
-            ;
             for (final var item : items.getItemMap(Conference.class).entrySet()) {
                 final Bookmark bookmark =
                         Bookmark.parseFromItem(item.getKey(), item.getValue(), account);

src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java 🔗

@@ -346,7 +346,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
         final SQLiteDatabase db = getWritableDatabase();
         final Stopwatch stopwatch = Stopwatch.createStarted();
         db.execSQL(COPY_PREEXISTING_ENTRIES);
-        Log.d(Config.LOGTAG, "rebuilt message index in " + stopwatch.stop().toString());
+        Log.d(Config.LOGTAG, "rebuilt message index in " + stopwatch.stop());
     }
 
     public static synchronized DatabaseBackend getInstance(Context context) {
@@ -1561,7 +1561,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
                         + " is not null and conversationUuid=(select uuid from conversations where"
                         + " accountUuid=? and (contactJid=? or contactJid like ?)) order by"
                         + " timeSent desc";
-        final String[] args = {account, jid.toString(), jid.toString() + "/%"};
+        final String[] args = {account, jid.toString(), jid + "/%"};
         Cursor cursor = db.rawQuery(SQL + (limit > 0 ? " limit " + limit : ""), args);
         List<FilePath> filesPaths = new ArrayList<>();
         while (cursor.moveToNext()) {

src/main/java/eu/siacs/conversations/persistance/FileBackend.java 🔗

@@ -425,9 +425,7 @@ public class FileBackend {
     }
 
     public static void updateFileParams(Message message, String url, long size) {
-        final StringBuilder body = new StringBuilder();
-        body.append(url).append('|').append(size);
-        message.setBody(body.toString());
+        message.setBody(url + '|' + size);
     }
 
     public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) {
@@ -1244,7 +1242,7 @@ public class FileBackend {
             Log.d(Config.LOGTAG, "settled on char length " + chars + " with quality=" + quality);
             final Avatar avatar = new Avatar();
             avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
-            avatar.image = new String(mByteArrayOutputStream.toByteArray());
+            avatar.image = mByteArrayOutputStream.toString();
             if (format.equals(Bitmap.CompressFormat.WEBP)) {
                 avatar.type = "image/webp";
             } else if (format.equals(Bitmap.CompressFormat.JPEG)) {
@@ -1289,7 +1287,7 @@ public class FileBackend {
             os.flush();
             os.close();
             avatar.sha1sum = CryptoHelper.bytesToHex(digest.digest());
-            avatar.image = new String(mByteArrayOutputStream.toByteArray());
+            avatar.image = mByteArrayOutputStream.toString();
             avatar.height = options.outHeight;
             avatar.width = options.outWidth;
             avatar.type = options.outMimeType;

src/main/java/eu/siacs/conversations/services/CallIntegration.java 🔗

@@ -540,10 +540,7 @@ public class CallIntegration extends Connection {
             return false;
         }
         // SailfishOS's AppSupport do not support Call Integration
-        if (Build.MODEL.endsWith("(AppSupport)")) {
-            return false;
-        }
-        return true;
+        return !Build.MODEL.endsWith("(AppSupport)");
     }
 
     public static boolean notSelfManaged(final Context context) {

src/main/java/eu/siacs/conversations/services/MessageArchiveService.java 🔗

@@ -3,15 +3,7 @@ package eu.siacs.conversations.services;
 import static eu.siacs.conversations.utils.Random.SECURE_RANDOM;
 
 import android.util.Log;
-
 import androidx.annotation.NonNull;
-
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
@@ -25,6 +17,11 @@ import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 import eu.siacs.conversations.xmpp.mam.MamReference;
 import im.conversations.android.xmpp.model.stanza.Iq;
 import im.conversations.android.xmpp.model.stanza.Message;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 
 public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 
@@ -90,7 +87,6 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
             }
             return null;
         }
-
     }
 
     MessageArchiveService(final XmppConnectionService service) {
@@ -106,11 +102,13 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
                 }
             }
         }
-        MamReference mamReference = MamReference.max(
-                mXmppConnectionService.databaseBackend.getLastMessageReceived(account),
-                mXmppConnectionService.databaseBackend.getLastClearDate(account)
-        );
-        mamReference = MamReference.max(mamReference, mXmppConnectionService.getAutomaticMessageDeletionDate());
+        MamReference mamReference =
+                MamReference.max(
+                        mXmppConnectionService.databaseBackend.getLastMessageReceived(account),
+                        mXmppConnectionService.databaseBackend.getLastClearDate(account));
+        mamReference =
+                MamReference.max(
+                        mamReference, mXmppConnectionService.getAutomaticMessageDeletionDate());
         long endCatchup = account.getXmppConnection().getLastSessionEstablished();
         final Query query;
         if (mamReference.getTimestamp() == 0) {
@@ -119,7 +117,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
             long startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
             List<Conversation> conversations = mXmppConnectionService.getConversations();
             for (Conversation conversation : conversations) {
-                if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted().getTimestamp()) {
+                if (conversation.getMode() == Conversation.MODE_SINGLE
+                        && conversation.getAccount() == account
+                        && startCatchup > conversation.getLastMessageTransmitted().getTimestamp()) {
                     this.query(conversation, startCatchup, true);
                 }
             }
@@ -134,27 +134,21 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
     }
 
     void catchupMUC(final Conversation conversation) {
-        if (conversation.getLastMessageTransmitted().getTimestamp() < 0 && conversation.countMessages() == 0) {
-            query(conversation,
-                    new MamReference(0),
-                    0,
-                    true);
+        if (conversation.getLastMessageTransmitted().getTimestamp() < 0
+                && conversation.countMessages() == 0) {
+            query(conversation, new MamReference(0), 0, true);
         } else {
-            query(conversation,
-                    conversation.getLastMessageTransmitted(),
-                    0,
-                    true);
+            query(conversation, conversation.getLastMessageTransmitted(), 0, true);
         }
     }
 
     public Query query(final Conversation conversation) {
-        if (conversation.getLastMessageTransmitted().getTimestamp() < 0 && conversation.countMessages() == 0) {
-            return query(conversation,
-                    new MamReference(0),
-                    System.currentTimeMillis(),
-                    false);
+        if (conversation.getLastMessageTransmitted().getTimestamp() < 0
+                && conversation.countMessages() == 0) {
+            return query(conversation, new MamReference(0), System.currentTimeMillis(), false);
         } else {
-            return query(conversation,
+            return query(
+                    conversation,
                     conversation.getLastMessageTransmitted(),
                     conversation.getAccount().getXmppConnection().getLastSessionEstablished(),
                     false);
@@ -168,7 +162,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
         } else {
             synchronized (this.queries) {
                 for (Query query : this.queries) {
-                    if (query.getAccount() == account && query.isCatchup() && ((conversation.getMode() == Conversation.MODE_SINGLE && query.getWith() == null) || query.getConversation() == conversation)) {
+                    if (query.getAccount() == account
+                            && query.isCatchup()
+                            && ((conversation.getMode() == Conversation.MODE_SINGLE
+                                            && query.getWith() == null)
+                                    || query.getConversation() == conversation)) {
                         return true;
                     }
                 }
@@ -178,21 +176,33 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
     }
 
     public Query query(final Conversation conversation, long end, boolean allowCatchup) {
-        return this.query(conversation, conversation.getLastMessageTransmitted(), end, allowCatchup);
+        return this.query(
+                conversation, conversation.getLastMessageTransmitted(), end, allowCatchup);
     }
 
-    public Query query(Conversation conversation, MamReference start, long end, boolean allowCatchup) {
+    public Query query(
+            Conversation conversation, MamReference start, long end, boolean allowCatchup) {
         synchronized (this.queries) {
             final Query query;
-            final MamReference startActual = MamReference.max(start, mXmppConnectionService.getAutomaticMessageDeletionDate());
+            final MamReference startActual =
+                    MamReference.max(
+                            start, mXmppConnectionService.getAutomaticMessageDeletionDate());
             if (start.getTimestamp() == 0) {
                 query = new Query(conversation, startActual, end, false);
                 query.reference = conversation.getFirstMamReference();
             } else {
                 if (allowCatchup) {
-                    MamReference maxCatchup = MamReference.max(startActual, System.currentTimeMillis() - Config.MAM_MAX_CATCHUP);
+                    MamReference maxCatchup =
+                            MamReference.max(
+                                    startActual,
+                                    System.currentTimeMillis() - Config.MAM_MAX_CATCHUP);
                     if (maxCatchup.greaterThan(startActual)) {
-                        Query reverseCatchup = new Query(conversation, startActual, maxCatchup.getTimestamp(), false);
+                        Query reverseCatchup =
+                                new Query(
+                                        conversation,
+                                        startActual,
+                                        maxCatchup.getTimestamp(),
+                                        false);
                         this.queries.add(reverseCatchup);
                         this.execute(reverseCatchup);
                     }
@@ -231,40 +241,57 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
         if (account.getStatus() == Account.State.ONLINE) {
             final Conversation conversation = query.getConversation();
             if (conversation != null && conversation.getStatus() == Conversation.STATUS_ARCHIVED) {
-                throw new IllegalStateException("Attempted to run MAM query for archived conversation");
-            }
-            Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": running mam query " + query.toString());
-            final Iq packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
-            this.mXmppConnectionService.sendIqPacket(account, packet, (p) -> {
-                final Element fin = p.findChild("fin", query.version.namespace);
-                if (p.getType() == Iq.Type.TIMEOUT) {
-                    synchronized (this.queries) {
-                        this.queries.remove(query);
-                        if (query.hasCallback()) {
-                            query.callback(false);
+                throw new IllegalStateException(
+                        "Attempted to run MAM query for archived conversation");
+            }
+            Log.d(
+                    Config.LOGTAG,
+                    account.getJid().asBareJid().toString() + ": running mam query " + query);
+            final Iq packet =
+                    this.mXmppConnectionService
+                            .getIqGenerator()
+                            .queryMessageArchiveManagement(query);
+            this.mXmppConnectionService.sendIqPacket(
+                    account,
+                    packet,
+                    (p) -> {
+                        final Element fin = p.findChild("fin", query.version.namespace);
+                        if (p.getType() == Iq.Type.TIMEOUT) {
+                            synchronized (this.queries) {
+                                this.queries.remove(query);
+                                if (query.hasCallback()) {
+                                    query.callback(false);
+                                }
+                            }
+                        } else if (p.getType() == Iq.Type.RESULT && fin != null) {
+                            final boolean running;
+                            synchronized (this.queries) {
+                                running = this.queries.contains(query);
+                            }
+                            if (running) {
+                                processFin(query, fin);
+                            } else {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        account.getJid().asBareJid()
+                                                + ": ignoring MAM iq result because query had been"
+                                                + " killed");
+                            }
+                        } else if (p.getType() == Iq.Type.RESULT && query.isLegacy()) {
+                            // do nothing
+                        } else {
+                            Log.d(
+                                    Config.LOGTAG,
+                                    account.getJid().asBareJid().toString()
+                                            + ": error executing mam: "
+                                            + p);
+                            try {
+                                finalizeQuery(query, true);
+                            } catch (final IllegalStateException e) {
+                                // ignored
+                            }
                         }
-                    }
-                } else if (p.getType() == Iq.Type.RESULT && fin != null) {
-                    final boolean running;
-                    synchronized (this.queries) {
-                        running = this.queries.contains(query);
-                    }
-                    if (running) {
-                        processFin(query, fin);
-                    } else {
-                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring MAM iq result because query had been killed");
-                    }
-                } else if (p.getType() == Iq.Type.RESULT && query.isLegacy()) {
-                    //do nothing
-                } else {
-                    Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": error executing mam: " + p.toString());
-                    try {
-                        finalizeQuery(query, true);
-                    } catch (final IllegalStateException e) {
-                        //ignored
-                    }
-                }
-            });
+                    });
         } else {
             synchronized (this.pendingQueries) {
                 this.pendingQueries.add(query);
@@ -320,7 +347,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
             for (Query query : queries) {
                 if (query.account == conversation.getAccount() && query.isCatchup()) {
                     final Jid with = query.getWith() == null ? null : query.getWith().asBareJid();
-                    if ((conversation.getMode() == Conversational.MODE_SINGLE && with == null) || (conversation.getJid().asBareJid().equals(with))) {
+                    if ((conversation.getMode() == Conversational.MODE_SINGLE && with == null)
+                            || (conversation.getJid().asBareJid().equals(with))) {
                         return true;
                     }
                 }
@@ -329,7 +357,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
         return false;
     }
 
-    boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
+    boolean queryInProgress(
+            Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
         synchronized (this.queries) {
             for (Query query : queries) {
                 if (query.conversation == conversation) {
@@ -361,12 +390,15 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
         String count = set == null ? null : set.findChildContent("count");
         Element first = set == null ? null : set.findChild("first");
         Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
-        boolean abort = (!query.isCatchup() && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
+        boolean abort =
+                (!query.isCatchup() && query.getTotalCount() >= Config.PAGE_SIZE)
+                        || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
         if (query.getConversation() != null) {
             query.getConversation().setFirstMamReference(first == null ? null : first.getContent());
         }
         if (complete || relevant == null || abort) {
-            //TODO: FIX done logic to look at complete. using count is probably unreliable because it can be ommited and doesn’t work with paging.
+            // TODO: FIX done logic to look at complete. using count is probably unreliable because
+            // it can be ommited and doesn’t work with paging.
             boolean done;
             if (query.isCatchup()) {
                 done = false;
@@ -384,9 +416,21 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
             done = done || (query.getActualMessageCount() == 0 && !query.isCatchup());
             this.finalizeQuery(query, done);
 
-            Log.d(Config.LOGTAG, query.getAccount().getJid().asBareJid() + ": finished mam after " + query.getTotalCount() + "(" + query.getActualMessageCount() + ") messages. messages left=" + !done + " count=" + count);
+            Log.d(
+                    Config.LOGTAG,
+                    query.getAccount().getJid().asBareJid()
+                            + ": finished mam after "
+                            + query.getTotalCount()
+                            + "("
+                            + query.getActualMessageCount()
+                            + ") messages. messages left="
+                            + !done
+                            + " count="
+                            + count);
             if (query.isCatchup() && query.getActualMessageCount() > 0) {
-                mXmppConnectionService.getNotificationService().finishBacklog(true, query.getAccount());
+                mXmppConnectionService
+                        .getNotificationService()
+                        .finishBacklog(true, query.getAccount());
             }
             processPostponed(query);
         } else {
@@ -407,11 +451,15 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
     void kill(final Conversation conversation) {
         final ArrayList<Query> toBeKilled = new ArrayList<>();
         synchronized (this.pendingQueries) {
-            for (final Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext(); ) {
+            for (final Iterator<Query> iterator = this.pendingQueries.iterator();
+                    iterator.hasNext(); ) {
                 final Query query = iterator.next();
                 if (query.getConversation() == conversation) {
                     iterator.remove();
-                    Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": killed pending MAM query for archived conversation");
+                    Log.d(
+                            Config.LOGTAG,
+                            conversation.getAccount().getJid().asBareJid()
+                                    + ": killed pending MAM query for archived conversation");
                 }
             }
         }
@@ -428,7 +476,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
     }
 
     private void kill(Query query) {
-        Log.d(Config.LOGTAG, query.getAccount().getJid().asBareJid() + ": killing mam query prematurely");
+        Log.d(
+                Config.LOGTAG,
+                query.getAccount().getJid().asBareJid() + ": killing mam query prematurely");
         query.callback = null;
         this.finalizeQuery(query, false);
         if (query.isCatchup() && query.getActualMessageCount() > 0) {
@@ -440,11 +490,20 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
     private void processPostponed(Query query) {
         query.account.getAxolotlService().processPostponed();
         query.pendingReceiptRequests.removeAll(query.receiptRequests);
-        Log.d(Config.LOGTAG, query.getAccount().getJid().asBareJid() + ": found " + query.pendingReceiptRequests.size() + " pending receipt requests");
+        Log.d(
+                Config.LOGTAG,
+                query.getAccount().getJid().asBareJid()
+                        + ": found "
+                        + query.pendingReceiptRequests.size()
+                        + " pending receipt requests");
         Iterator<ReceiptRequest> iterator = query.pendingReceiptRequests.iterator();
         while (iterator.hasNext()) {
             ReceiptRequest rr = iterator.next();
-            mXmppConnectionService.sendMessagePacket(query.account, mXmppConnectionService.getMessageGenerator().received(query.account, rr.getJid(), rr.getId()));
+            mXmppConnectionService.sendMessagePacket(
+                    query.account,
+                    mXmppConnectionService
+                            .getMessageGenerator()
+                            .received(query.account, rr.getJid(), rr.getId()));
             iterator.remove();
         }
     }
@@ -465,7 +524,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 
     @Override
     public void onAdvancedStreamFeaturesAvailable(Account account) {
-        if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+        if (account.getXmppConnection() != null
+                && account.getXmppConnection().getFeatures().mam()) {
             this.catchup(account);
         }
     }
@@ -492,9 +552,12 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
         private boolean catchup = true;
         public final Version version;
 
-
         Query(Conversation conversation, MamReference start, long end, boolean catchup) {
-            this(conversation.getAccount(), Version.get(conversation.getAccount(), conversation), catchup ? start : start.timeOnly(), end);
+            this(
+                    conversation.getAccount(),
+                    Version.get(conversation.getAccount(), conversation),
+                    catchup ? start : start.timeOnly(),
+                    end);
             this.conversation = conversation;
             this.pagingOrder = catchup ? PagingOrder.NORMAL : PagingOrder.REVERSE;
             this.catchup = catchup;
@@ -517,7 +580,12 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
         }
 
         private Query page(String reference) {
-            Query query = new Query(this.account, this.version, new MamReference(this.start, reference), this.end);
+            Query query =
+                    new Query(
+                            this.account,
+                            this.version,
+                            new MamReference(this.start, reference),
+                            this.end);
             query.conversation = conversation;
             query.totalCount = totalCount;
             query.actualCount = actualCount;

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -507,7 +507,7 @@ public class XmppConnectionService extends Service {
     private LruCache<String, Bitmap> mBitmapCache;
     private final BroadcastReceiver mInternalEventReceiver = new InternalEventReceiver();
     private final BroadcastReceiver mInternalRestrictedEventReceiver =
-            new RestrictedEventReceiver(Arrays.asList(TorServiceUtils.ACTION_STATUS));
+            new RestrictedEventReceiver(List.of(TorServiceUtils.ACTION_STATUS));
     private final BroadcastReceiver mInternalScreenEventReceiver = new InternalEventReceiver();
 
     private static String generateFetchKey(Account account, final Avatar avatar) {
@@ -4535,7 +4535,7 @@ public class XmppConnectionService extends Service {
                                         if (packet.getType() == Iq.Type.RESULT) {
                                             callback.onPushSucceeded();
                                         } else {
-                                            Log.d(Config.LOGTAG, "failed: " + packet.toString());
+                                            Log.d(Config.LOGTAG, "failed: " + packet);
                                             callback.onPushFailed();
                                         }
                                     }
@@ -5125,7 +5125,7 @@ public class XmppConnectionService extends Service {
                         if (error == null) {
                             Log.d(Config.LOGTAG, ERROR + "(server error)");
                         } else {
-                            Log.d(Config.LOGTAG, ERROR + error.toString());
+                            Log.d(Config.LOGTAG, ERROR + error);
                         }
                     }
                     if (callback != null) {

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -531,8 +531,7 @@ public class ConversationFragment extends XmppFragment
                 @Override
                 public void onClick(View v) {
                     Object tag = v.getTag();
-                    if (tag instanceof SendButtonAction) {
-                        SendButtonAction action = (SendButtonAction) tag;
+                    if (tag instanceof SendButtonAction action) {
                         switch (action) {
                             case TAKE_PHOTO:
                             case RECORD_VIDEO:

src/main/java/eu/siacs/conversations/ui/CreatePrivateGroupChatDialog.java 🔗

@@ -4,20 +4,15 @@ import android.app.Dialog;
 import android.content.Context;
 import android.os.Bundle;
 import android.widget.AutoCompleteTextView;
-
 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.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.DialogCreateConferenceBinding;
 import eu.siacs.conversations.ui.util.DelayedHintHelper;
+import java.util.ArrayList;
+import java.util.List;
 
 public class CreatePrivateGroupChatDialog extends DialogFragment {
 
@@ -41,23 +36,36 @@ public class CreatePrivateGroupChatDialog extends DialogFragment {
     @NonNull
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
-        final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
+        final MaterialAlertDialogBuilder builder =
+                new MaterialAlertDialogBuilder(requireActivity());
         builder.setTitle(R.string.create_private_group_chat);
-        final DialogCreateConferenceBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_create_conference, null, false);
+        final DialogCreateConferenceBinding binding =
+                DataBindingUtil.inflate(
+                        getActivity().getLayoutInflater(),
+                        R.layout.dialog_create_conference,
+                        null,
+                        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(R.string.choose_participants, (dialog, which) -> mListener.onCreateDialogPositiveClick(binding.account, binding.groupChatName.getText().toString().trim()));
+        builder.setPositiveButton(
+                R.string.choose_participants,
+                (dialog, which) ->
+                        mListener.onCreateDialogPositiveClick(
+                                binding.account,
+                                binding.groupChatName.getText().toString().trim()));
         builder.setNegativeButton(R.string.cancel, null);
         DelayedHintHelper.setHint(R.string.providing_a_name_is_optional, binding.groupChatName);
-        binding.groupChatName.setOnEditorActionListener((v, actionId, event) -> {
-            mListener.onCreateDialogPositiveClick(binding.account, binding.groupChatName.getText().toString().trim());
-            return true;
-        });
+        binding.groupChatName.setOnEditorActionListener(
+                (v, actionId, event) -> {
+                    mListener.onCreateDialogPositiveClick(
+                            binding.account, binding.groupChatName.getText().toString().trim());
+                    return true;
+                });
         return builder.create();
     }
 
-
     public interface CreateConferenceDialogListener {
         void onCreateDialogPositiveClick(AutoCompleteTextView spinner, String subject);
     }
@@ -68,8 +76,8 @@ public class CreatePrivateGroupChatDialog extends DialogFragment {
         try {
             mListener = (CreateConferenceDialogListener) context;
         } catch (ClassCastException e) {
-            throw new ClassCastException(context.toString()
-                    + " must implement CreateConferenceDialogListener");
+            throw new ClassCastException(
+                    context + " must implement CreateConferenceDialogListener");
         }
     }
 

src/main/java/eu/siacs/conversations/ui/CreatePublicChannelDialog.java 🔗

@@ -288,7 +288,7 @@ public class CreatePublicChannelDialog extends DialogFragment implements OnBacke
             mListener = (CreatePublicChannelDialogListener) context;
         } catch (ClassCastException e) {
             throw new ClassCastException(
-                    context.toString() + " must implement CreateConferenceDialogListener");
+                    context + " must implement CreateConferenceDialogListener");
         }
     }
 

src/main/java/eu/siacs/conversations/ui/JoinConferenceDialog.java 🔗

@@ -6,118 +6,136 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
 import android.widget.AutoCompleteTextView;
-import android.widget.Spinner;
-
 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.android.material.textfield.TextInputLayout;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.databinding.DialogJoinConferenceBinding;
 import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
 import eu.siacs.conversations.ui.util.DelayedHintHelper;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 public class JoinConferenceDialog extends DialogFragment implements OnBackendConnected {
 
-	private static final String PREFILLED_JID_KEY = "prefilled_jid";
-	private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
-	private JoinConferenceDialogListener mListener;
-	private KnownHostsAdapter knownHostsAdapter;
-
-	public static JoinConferenceDialog newInstance(String prefilledJid, List<String> accounts) {
-		JoinConferenceDialog dialog = new JoinConferenceDialog();
-		Bundle bundle = new Bundle();
-		bundle.putString(PREFILLED_JID_KEY, prefilledJid);
-		bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) accounts);
-		dialog.setArguments(bundle);
-		return dialog;
-	}
-
-	@Override
-	public void onActivityCreated(Bundle savedInstanceState) {
-		super.onActivityCreated(savedInstanceState);
-		setRetainInstance(true);
-	}
-
-	@NonNull
-	@Override
-	public Dialog onCreateDialog(Bundle savedInstanceState) {
-		final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
-		builder.setTitle(R.string.join_public_channel);
-		final DialogJoinConferenceBinding binding = DataBindingUtil.inflate(getActivity().getLayoutInflater(), R.layout.dialog_join_conference, null, false);
-		DelayedHintHelper.setHint(R.string.channel_full_jid_example, binding.jid);
-		this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
-		binding.jid.setAdapter(knownHostsAdapter);
-		String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
-		if (prefilledJid != null) {
-			binding.jid.append(prefilledJid);
-		}
-		StartConversationActivity.populateAccountSpinner(getActivity(), getArguments().getStringArrayList(ACCOUNTS_LIST_KEY), binding.account);
-		builder.setView(binding.getRoot());
-		builder.setPositiveButton(R.string.join, null);
-		builder.setNegativeButton(R.string.cancel, null);
-		AlertDialog dialog = builder.create();
-		dialog.show();
-		dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(view -> mListener.onJoinDialogPositiveClick(dialog, binding.account, binding.accountJidLayout, binding.jid));
-		binding.jid.setOnEditorActionListener((v, actionId, event) -> {
-			mListener.onJoinDialogPositiveClick(dialog, binding.account, binding.accountJidLayout, binding.jid);
-			return true;
-		});
-		return dialog;
-	}
-
-	@Override
-	public void onBackendConnected() {
-		refreshKnownHosts();
-	}
-
-	private void refreshKnownHosts() {
-		Activity activity = getActivity();
-		if (activity instanceof XmppActivity) {
-			Collection<String> hosts = ((XmppActivity) activity).xmppConnectionService.getKnownConferenceHosts();
-			this.knownHostsAdapter.refresh(hosts);
-		}
-	}
-
-	@Override
-	public void onAttach(@NonNull final Context context) {
-		super.onAttach(context);
-		try {
-			mListener = (JoinConferenceDialogListener) context;
-		} catch (ClassCastException e) {
-			throw new ClassCastException(context.toString()
-					+ " must implement JoinConferenceDialogListener");
-		}
-	}
-
-	@Override
-	public void onDestroyView() {
-		Dialog dialog = getDialog();
-		if (dialog != null && getRetainInstance()) {
-			dialog.setDismissMessage(null);
-		}
-		super.onDestroyView();
-	}
-
-	@Override
-	public void onStart() {
-		super.onStart();
-		final Activity activity = getActivity();
-		if (activity instanceof XmppActivity && ((XmppActivity) activity).xmppConnectionService != null) {
-			refreshKnownHosts();
-		}
-	}
-
-	public interface JoinConferenceDialogListener {
-		void onJoinDialogPositiveClick(Dialog dialog, AutoCompleteTextView spinner, TextInputLayout jidLayout, AutoCompleteTextView jid);
-	}
+    private static final String PREFILLED_JID_KEY = "prefilled_jid";
+    private static final String ACCOUNTS_LIST_KEY = "activated_accounts_list";
+    private JoinConferenceDialogListener mListener;
+    private KnownHostsAdapter knownHostsAdapter;
+
+    public static JoinConferenceDialog newInstance(String prefilledJid, List<String> accounts) {
+        JoinConferenceDialog dialog = new JoinConferenceDialog();
+        Bundle bundle = new Bundle();
+        bundle.putString(PREFILLED_JID_KEY, prefilledJid);
+        bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) accounts);
+        dialog.setArguments(bundle);
+        return dialog;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        setRetainInstance(true);
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final MaterialAlertDialogBuilder builder =
+                new MaterialAlertDialogBuilder(requireActivity());
+        builder.setTitle(R.string.join_public_channel);
+        final DialogJoinConferenceBinding binding =
+                DataBindingUtil.inflate(
+                        getActivity().getLayoutInflater(),
+                        R.layout.dialog_join_conference,
+                        null,
+                        false);
+        DelayedHintHelper.setHint(R.string.channel_full_jid_example, binding.jid);
+        this.knownHostsAdapter = new KnownHostsAdapter(getActivity(), R.layout.item_autocomplete);
+        binding.jid.setAdapter(knownHostsAdapter);
+        String prefilledJid = getArguments().getString(PREFILLED_JID_KEY);
+        if (prefilledJid != null) {
+            binding.jid.append(prefilledJid);
+        }
+        StartConversationActivity.populateAccountSpinner(
+                getActivity(),
+                getArguments().getStringArrayList(ACCOUNTS_LIST_KEY),
+                binding.account);
+        builder.setView(binding.getRoot());
+        builder.setPositiveButton(R.string.join, null);
+        builder.setNegativeButton(R.string.cancel, null);
+        AlertDialog dialog = builder.create();
+        dialog.show();
+        dialog.getButton(DialogInterface.BUTTON_POSITIVE)
+                .setOnClickListener(
+                        view ->
+                                mListener.onJoinDialogPositiveClick(
+                                        dialog,
+                                        binding.account,
+                                        binding.accountJidLayout,
+                                        binding.jid));
+        binding.jid.setOnEditorActionListener(
+                (v, actionId, event) -> {
+                    mListener.onJoinDialogPositiveClick(
+                            dialog, binding.account, binding.accountJidLayout, binding.jid);
+                    return true;
+                });
+        return dialog;
+    }
+
+    @Override
+    public void onBackendConnected() {
+        refreshKnownHosts();
+    }
+
+    private void refreshKnownHosts() {
+        Activity activity = getActivity();
+        if (activity instanceof XmppActivity) {
+            Collection<String> hosts =
+                    ((XmppActivity) activity).xmppConnectionService.getKnownConferenceHosts();
+            this.knownHostsAdapter.refresh(hosts);
+        }
+    }
+
+    @Override
+    public void onAttach(@NonNull final Context context) {
+        super.onAttach(context);
+        try {
+            mListener = (JoinConferenceDialogListener) context;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(context + " must implement JoinConferenceDialogListener");
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        Dialog dialog = getDialog();
+        if (dialog != null && getRetainInstance()) {
+            dialog.setDismissMessage(null);
+        }
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        final Activity activity = getActivity();
+        if (activity instanceof XmppActivity
+                && ((XmppActivity) activity).xmppConnectionService != null) {
+            refreshKnownHosts();
+        }
+    }
+
+    public interface JoinConferenceDialogListener {
+        void onJoinDialogPositiveClick(
+                Dialog dialog,
+                AutoCompleteTextView spinner,
+                TextInputLayout jidLayout,
+                AutoCompleteTextView jid);
+    }
 }

src/main/java/eu/siacs/conversations/ui/ShortcutActivity.java 🔗

@@ -5,50 +5,57 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.inputmethod.InputMethodManager;
-
 import androidx.appcompat.app.ActionBar;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.ListItem;
+import java.util.Collections;
+import java.util.List;
 
 public class ShortcutActivity extends AbstractSearchableListItemActivity {
 
-    private static final List<String> BLACKLISTED_ACTIVITIES = Arrays.asList("com.teslacoilsw.launcher.ChooseActionIntentActivity");
+    private static final List<String> BLACKLISTED_ACTIVITIES =
+            List.of("com.teslacoilsw.launcher.ChooseActionIntentActivity");
 
     @Override
-    protected void refreshUiReal() {
-
-    }
+    protected void refreshUiReal() {}
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        getListView().setOnItemClickListener((parent, view, position, id) -> {
-
-            final ComponentName callingActivity = getCallingActivity();
+        getListView()
+                .setOnItemClickListener(
+                        (parent, view, position, id) -> {
+                            final ComponentName callingActivity = getCallingActivity();
 
-            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);
 
-            ListItem listItem = getListItems().get(position);
-            final boolean legacy = BLACKLISTED_ACTIVITIES.contains(callingActivity == null ? null : callingActivity.getClassName());
-            Intent shortcut = xmppConnectionService.getShortcutService().createShortcut(((Contact) listItem), legacy);
-            setResult(RESULT_OK,shortcut);
-            finish();
-        });
+                            ListItem listItem = getListItems().get(position);
+                            final boolean legacy =
+                                    BLACKLISTED_ACTIVITIES.contains(
+                                            callingActivity == null
+                                                    ? null
+                                                    : callingActivity.getClassName());
+                            Intent shortcut =
+                                    xmppConnectionService
+                                            .getShortcutService()
+                                            .createShortcut(((Contact) listItem), legacy);
+                            setResult(RESULT_OK, shortcut);
+                            finish();
+                        });
     }
 
     @Override
     public void onStart() {
         super.onStart();
         ActionBar bar = getSupportActionBar();
-        if(bar != null){
+        if (bar != null) {
             bar.setTitle(R.string.create_shortcut);
         }
     }
@@ -63,8 +70,7 @@ public class ShortcutActivity extends AbstractSearchableListItemActivity {
         for (final Account account : xmppConnectionService.getAccounts()) {
             if (account.isEnabled()) {
                 for (final Contact contact : account.getRoster().getContacts()) {
-                    if (contact.showInContactList()
-                            && contact.match(this, needle)) {
+                    if (contact.showInContactList() && contact.match(this, needle)) {
                         getListItems().add(contact);
                     }
                 }

src/main/java/eu/siacs/conversations/ui/XmppActivity.java 🔗

@@ -181,8 +181,7 @@ public abstract class XmppActivity extends ActionBarActivity {
     private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
         if (imageView != null) {
             final Drawable drawable = imageView.getDrawable();
-            if (drawable instanceof AsyncDrawable) {
-                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+            if (drawable instanceof AsyncDrawable asyncDrawable) {
                 return asyncDrawable.getBitmapWorkerTask();
             }
         }

src/main/java/eu/siacs/conversations/ui/adapter/KnownHostsAdapter.java 🔗

@@ -30,7 +30,7 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
                         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());
+                            builder.add(local + '@' + Config.QUICKSY_DOMAIN);
                         } else {
                             for (String domain : domains) {
                                 builder.add(local + '@' + domain);

src/main/java/eu/siacs/conversations/ui/adapter/MediaAdapter.java 🔗

@@ -77,10 +77,8 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
         this.mediaSize = Math.round(activity.getResources().getDimension(mediaSize));
     }
 
-    @SuppressWarnings("rawtypes")
-    public static void setMediaSize(final RecyclerView recyclerView, int mediaSize) {
-        final RecyclerView.Adapter adapter = recyclerView.getAdapter();
-        if (adapter instanceof MediaAdapter mediaAdapter) {
+    public static void setMediaSize(final RecyclerView recyclerView, final int mediaSize) {
+        if (recyclerView.getAdapter() instanceof MediaAdapter mediaAdapter) {
             mediaAdapter.setMediaSize(mediaSize);
         }
     }
@@ -137,6 +135,9 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
             return R.drawable.ic_code_48dp;
         } else if (mime.equals("message/rfc822")) {
             return R.drawable.ic_email_48dp;
+        } else if (Arrays.asList("application/x-pcapng", "application/vnd.tcpdump.pcap")
+                .contains(mime)) {
+            return R.drawable.ic_lan_24dp;
         } else {
             return R.drawable.ic_help_center_48dp;
         }
@@ -254,7 +255,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
         }
     }
 
-    static class MediaViewHolder extends RecyclerView.ViewHolder {
+    public static class MediaViewHolder extends RecyclerView.ViewHolder {
 
         private final ItemMediaBinding binding;
 

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -1485,7 +1485,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 
     private abstract static class MessageItemViewHolder /*extends RecyclerView.ViewHolder*/ {
 
-        private View itemView;
+        private final View itemView;
 
         private MessageItemViewHolder(@NonNull View itemView) {
             this.itemView = itemView;

src/main/java/eu/siacs/conversations/ui/service/CameraManager.java 🔗

@@ -25,9 +25,8 @@ import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.PreviewCallback;
 import android.util.Log;
 import android.view.TextureView;
-
 import com.google.zxing.PlanarYUVLuminanceSource;
-
+import eu.siacs.conversations.Config;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -35,8 +34,6 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import eu.siacs.conversations.Config;
-
 /**
  * @author Andreas Schildbach
  */
@@ -69,7 +66,10 @@ public final class CameraManager {
         return cameraInfo.orientation;
     }
 
-    public Camera open(final TextureView textureView, final int displayOrientation, final boolean continuousAutoFocus)
+    public Camera open(
+            final TextureView textureView,
+            final int displayOrientation,
+            final boolean continuousAutoFocus)
             throws IOException {
         final int cameraId = determineCameraId();
         Camera.getCameraInfo(cameraId, cameraInfo);
@@ -80,8 +80,7 @@ public final class CameraManager {
             camera.setDisplayOrientation((720 - displayOrientation - cameraInfo.orientation) % 360);
         else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
             camera.setDisplayOrientation((720 - displayOrientation + cameraInfo.orientation) % 360);
-        else
-            throw new IllegalStateException("facing: " + cameraInfo.facing);
+        else throw new IllegalStateException("facing: " + cameraInfo.facing);
 
         camera.setPreviewTexture(textureView.getSurfaceTexture());
 
@@ -105,18 +104,22 @@ public final class CameraManager {
         boolean isTexturePortrait = width < height;
         boolean isCameraPortrait = cameraResolution.width < cameraResolution.height;
         if (isTexturePortrait == isCameraPortrait) {
-            widthFactor = (float)cameraResolution.width / width;
-            heightFactor = (float)cameraResolution.height / height;
+            widthFactor = (float) cameraResolution.width / width;
+            heightFactor = (float) cameraResolution.height / height;
             orientedFrame = new Rect(frame);
         } else {
-            widthFactor = (float)cameraResolution.width / height;
-            heightFactor = (float)cameraResolution.height / width;
+            widthFactor = (float) cameraResolution.width / height;
+            heightFactor = (float) cameraResolution.height / width;
             // Swap X and Y coordinates to flip frame to the same orientation as cameraResolution
             orientedFrame = new Rect(frame.top, frame.left, frame.bottom, frame.right);
         }
 
-        framePreview = new RectF(orientedFrame.left * widthFactor, orientedFrame.top * heightFactor,
-                orientedFrame.right * widthFactor, orientedFrame.bottom * heightFactor);
+        framePreview =
+                new RectF(
+                        orientedFrame.left * widthFactor,
+                        orientedFrame.top * heightFactor,
+                        orientedFrame.right * widthFactor,
+                        orientedFrame.bottom * heightFactor);
 
         final String savedParameters = parameters == null ? null : parameters.flatten();
 
@@ -130,7 +133,7 @@ public final class CameraManager {
                     camera.setParameters(parameters2);
                     setDesiredCameraParameters(camera, cameraResolution, continuousAutoFocus);
                 } catch (final RuntimeException x2) {
-                    Log.d(Config.LOGTAG,"problem setting camera parameters", x2);
+                    Log.d(Config.LOGTAG, "problem setting camera parameters", x2);
                 }
             }
         }
@@ -139,7 +142,7 @@ public final class CameraManager {
             camera.startPreview();
             return camera;
         } catch (final RuntimeException x) {
-            Log.w(Config.LOGTAG,"something went wrong while starting camera preview", x);
+            Log.w(Config.LOGTAG, "something went wrong while starting camera preview", x);
             camera.release();
             throw x;
         }
@@ -152,15 +155,13 @@ public final class CameraManager {
         // prefer back-facing camera
         for (int i = 0; i < cameraCount; i++) {
             Camera.getCameraInfo(i, cameraInfo);
-            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
-                return i;
+            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) return i;
         }
 
         // fall back to front-facing camera
         for (int i = 0; i < cameraCount; i++) {
             Camera.getCameraInfo(i, cameraInfo);
-            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
-                return i;
+            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) return i;
         }
 
         return -1;
@@ -171,29 +172,28 @@ public final class CameraManager {
             try {
                 camera.stopPreview();
             } catch (final RuntimeException x) {
-                Log.w(Config.LOGTAG,"something went wrong while stopping camera preview", x);
+                Log.w(Config.LOGTAG, "something went wrong while stopping camera preview", x);
             }
 
             camera.release();
         }
     }
 
-    private static final Comparator<Camera.Size> numPixelComparator = new Comparator<Camera.Size>() {
-        @Override
-        public int compare(final Camera.Size size1, final Camera.Size size2) {
-            final int pixels1 = size1.height * size1.width;
-            final int pixels2 = size2.height * size2.width;
-
-            if (pixels1 < pixels2)
-                return 1;
-            else if (pixels1 > pixels2)
-                return -1;
-            else
-                return 0;
-        }
-    };
+    private static final Comparator<Camera.Size> numPixelComparator =
+            new Comparator<Camera.Size>() {
+                @Override
+                public int compare(final Camera.Size size1, final Camera.Size size2) {
+                    final int pixels1 = size1.height * size1.width;
+                    final int pixels2 = size2.height * size2.width;
+
+                    if (pixels1 < pixels2) return 1;
+                    else if (pixels1 > pixels2) return -1;
+                    else return 0;
+                }
+            };
 
-    private static Camera.Size findBestPreviewSizeValue(final Camera.Parameters parameters, int width, int height) {
+    private static Camera.Size findBestPreviewSizeValue(
+            final Camera.Parameters parameters, int width, int height) {
         if (height > width) {
             final int temp = width;
             width = height;
@@ -203,11 +203,11 @@ public final class CameraManager {
         final float screenAspectRatio = (float) width / (float) height;
 
         final List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
-        if (rawSupportedSizes == null)
-            return parameters.getPreviewSize();
+        if (rawSupportedSizes == null) return parameters.getPreviewSize();
 
         // sort by size, descending
-        final List<Camera.Size> supportedPreviewSizes = new ArrayList<Camera.Size>(rawSupportedSizes);
+        final List<Camera.Size> supportedPreviewSizes =
+                new ArrayList<Camera.Size>(rawSupportedSizes);
         Collections.sort(supportedPreviewSizes, numPixelComparator);
 
         Camera.Size bestSize = null;
@@ -217,8 +217,7 @@ public final class CameraManager {
             final int realWidth = supportedPreviewSize.width;
             final int realHeight = supportedPreviewSize.height;
             final int realPixels = realWidth * realHeight;
-            if (realPixels < MIN_PREVIEW_PIXELS || realPixels > MAX_PREVIEW_PIXELS)
-                continue;
+            if (realPixels < MIN_PREVIEW_PIXELS || realPixels > MAX_PREVIEW_PIXELS) continue;
 
             final boolean isCandidatePortrait = realWidth < realHeight;
             final int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
@@ -234,27 +233,32 @@ public final class CameraManager {
             }
         }
 
-        if (bestSize != null)
-            return bestSize;
-        else
-            return parameters.getPreviewSize();
+        if (bestSize != null) return bestSize;
+        else return parameters.getPreviewSize();
     }
 
     @SuppressLint("InlinedApi")
-    private static void setDesiredCameraParameters(final Camera camera, final Camera.Size cameraResolution,
+    private static void setDesiredCameraParameters(
+            final Camera camera,
+            final Camera.Size cameraResolution,
             final boolean continuousAutoFocus) {
         final Camera.Parameters parameters = camera.getParameters();
-        if (parameters == null)
-            return;
+        if (parameters == null) return;
 
         final List<String> supportedFocusModes = parameters.getSupportedFocusModes();
-        final String focusMode = continuousAutoFocus
-                ? findValue(supportedFocusModes, Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
-                        Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, Camera.Parameters.FOCUS_MODE_AUTO,
-                        Camera.Parameters.FOCUS_MODE_MACRO)
-                : findValue(supportedFocusModes, Camera.Parameters.FOCUS_MODE_AUTO, Camera.Parameters.FOCUS_MODE_MACRO);
-        if (focusMode != null)
-            parameters.setFocusMode(focusMode);
+        final String focusMode =
+                continuousAutoFocus
+                        ? findValue(
+                                supportedFocusModes,
+                                Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
+                                Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
+                                Camera.Parameters.FOCUS_MODE_AUTO,
+                                Camera.Parameters.FOCUS_MODE_MACRO)
+                        : findValue(
+                                supportedFocusModes,
+                                Camera.Parameters.FOCUS_MODE_AUTO,
+                                Camera.Parameters.FOCUS_MODE_MACRO);
+        if (focusMode != null) parameters.setFocusMode(focusMode);
 
         parameters.setPreviewSize(cameraResolution.width, cameraResolution.height);
 
@@ -265,26 +269,31 @@ public final class CameraManager {
         try {
             camera.setOneShotPreviewCallback(callback);
         } catch (final RuntimeException x) {
-            Log.d(Config.LOGTAG,"problem requesting preview frame, callback won't be called", x);
+            Log.d(Config.LOGTAG, "problem requesting preview frame, callback won't be called", x);
         }
     }
 
     public PlanarYUVLuminanceSource buildLuminanceSource(final byte[] data) {
-        return new PlanarYUVLuminanceSource(data, cameraResolution.width, cameraResolution.height,
-                (int) framePreview.left, (int) framePreview.top, (int) framePreview.width(),
-                (int) framePreview.height(), false);
+        return new PlanarYUVLuminanceSource(
+                data,
+                cameraResolution.width,
+                cameraResolution.height,
+                (int) framePreview.left,
+                (int) framePreview.top,
+                (int) framePreview.width(),
+                (int) framePreview.height(),
+                false);
     }
 
     public void setTorch(final boolean enabled) {
-        if (enabled != getTorchEnabled(camera))
-            setTorchEnabled(camera, enabled);
+        if (enabled != getTorchEnabled(camera)) setTorchEnabled(camera, enabled);
     }
 
     private static boolean getTorchEnabled(final Camera camera) {
         final Camera.Parameters parameters = camera.getParameters();
         if (parameters != null) {
             final String flashMode = camera.getParameters().getFlashMode();
-            return flashMode != null && (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)
+            return (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)
                     || Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
         }
 
@@ -298,10 +307,12 @@ public final class CameraManager {
         if (supportedFlashModes != null) {
             final String flashMode;
             if (enabled)
-                flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_TORCH,
-                        Camera.Parameters.FLASH_MODE_ON);
-            else
-                flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
+                flashMode =
+                        findValue(
+                                supportedFlashModes,
+                                Camera.Parameters.FLASH_MODE_TORCH,
+                                Camera.Parameters.FLASH_MODE_ON);
+            else flashMode = findValue(supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
 
             if (flashMode != null) {
                 camera.cancelAutoFocus(); // autofocus can cause conflict
@@ -314,8 +325,7 @@ public final class CameraManager {
 
     private static String findValue(final Collection<String> values, final String... valuesToFind) {
         for (final String valueToFind : valuesToFind)
-            if (values.contains(valueToFind))
-                return valueToFind;
+            if (values.contains(valueToFind)) return valueToFind;
 
         return null;
     }

src/main/java/eu/siacs/conversations/ui/util/AvatarWorkerTask.java 🔗

@@ -7,22 +7,18 @@ import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.widget.ImageView;
-
 import androidx.annotation.DimenRes;
-
-import java.lang.ref.WeakReference;
-import java.util.concurrent.RejectedExecutionException;
-
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.services.AvatarService;
 import eu.siacs.conversations.ui.XmppActivity;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.RejectedExecutionException;
 
 public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void, Bitmap> {
     private final WeakReference<ImageView> imageViewReference;
     private AvatarService.Avatarable avatarable = null;
-    private @DimenRes
-    final int size;
+    private @DimenRes final int size;
 
     public AvatarWorkerTask(ImageView imageView, @DimenRes int size) {
         imageViewReference = new WeakReference<>(imageView);
@@ -36,7 +32,8 @@ public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void,
         if (activity == null) {
             return null;
         }
-        return activity.avatarService().get(avatarable, (int) activity.getResources().getDimension(size), isCancelled());
+        return activity.avatarService()
+                .get(avatarable, (int) activity.getResources().getDimension(size), isCancelled());
     }
 
     @Override
@@ -50,11 +47,12 @@ public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void,
         }
     }
 
-    public static boolean cancelPotentialWork(AvatarService.Avatarable avatarable, ImageView imageView) {
+    public static boolean cancelPotentialWork(
+            AvatarService.Avatarable avatarable, ImageView imageView) {
         final AvatarWorkerTask workerTask = getBitmapWorkerTask(imageView);
 
         if (workerTask != null) {
-            final AvatarService.Avatarable old= workerTask.avatarable;
+            final AvatarService.Avatarable old = workerTask.avatarable;
             if (old == null || avatarable != old) {
                 workerTask.cancel(true);
             } else {
@@ -67,21 +65,28 @@ public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void,
     public static AvatarWorkerTask getBitmapWorkerTask(ImageView imageView) {
         if (imageView != null) {
             final Drawable drawable = imageView.getDrawable();
-            if (drawable instanceof AsyncDrawable) {
-                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+            if (drawable instanceof AsyncDrawable asyncDrawable) {
                 return asyncDrawable.getAvatarWorkerTask();
             }
         }
         return null;
     }
 
-    public static void loadAvatar(final AvatarService.Avatarable avatarable, final ImageView imageView, final @DimenRes int size) {
+    public static void loadAvatar(
+            final AvatarService.Avatarable avatarable,
+            final ImageView imageView,
+            final @DimenRes int size) {
         if (cancelPotentialWork(avatarable, imageView)) {
             final XmppActivity activity = XmppActivity.find(imageView);
             if (activity == null) {
                 return;
             }
-            final Bitmap bm = activity.avatarService().get(avatarable, (int) activity.getResources().getDimension(size), true);
+            final Bitmap bm =
+                    activity.avatarService()
+                            .get(
+                                    avatarable,
+                                    (int) activity.getResources().getDimension(size),
+                                    true);
             setContentDescription(avatarable, imageView);
             if (bm != null) {
                 cancelPotentialWork(avatarable, imageView);
@@ -91,7 +96,8 @@ public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void,
                 imageView.setBackgroundColor(avatarable.getAvatarBackgroundColor());
                 imageView.setImageDrawable(null);
                 final AvatarWorkerTask task = new AvatarWorkerTask(imageView, size);
-                final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+                final AsyncDrawable asyncDrawable =
+                        new AsyncDrawable(activity.getResources(), null, task);
                 imageView.setImageDrawable(asyncDrawable);
                 try {
                     task.execute(avatarable);
@@ -101,12 +107,14 @@ public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void,
         }
     }
 
-    private static void setContentDescription(final AvatarService.Avatarable avatarable, final ImageView imageView) {
+    private static void setContentDescription(
+            final AvatarService.Avatarable avatarable, final ImageView imageView) {
         final Context context = imageView.getContext();
         if (avatarable instanceof Account) {
             imageView.setContentDescription(context.getString(R.string.your_avatar));
         } else {
-            imageView.setContentDescription(context.getString(R.string.avatar_for_x, avatarable.getAvatarName()));
+            imageView.setContentDescription(
+                    context.getString(R.string.avatar_for_x, avatarable.getAvatarName()));
         }
     }
 

src/main/java/eu/siacs/conversations/ui/util/MucDetailsContextMenuHelper.java 🔗

@@ -9,11 +9,7 @@ import android.view.ContextMenu;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-
-import androidx.appcompat.app.AlertDialog;
-
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
@@ -29,15 +25,13 @@ import eu.siacs.conversations.ui.MucUsersActivity;
 import eu.siacs.conversations.ui.XmppActivity;
 import eu.siacs.conversations.xmpp.Jid;
 
-
 public final class MucDetailsContextMenuHelper {
 
     public static void onCreateContextMenu(ContextMenu menu, View v) {
         final XmppActivity activity = XmppActivity.find(v);
         final Object tag = v.getTag();
-        if (tag instanceof MucOptions.User && activity != null) {
+        if (tag instanceof User user && activity != null) {
             activity.getMenuInflater().inflate(R.menu.muc_details_context, menu);
-            final MucOptions.User user = (MucOptions.User) tag;
             String name;
             final Contact contact = user.getContact();
             if (contact != null && contact.showInContactList()) {
@@ -48,13 +42,17 @@ public final class MucDetailsContextMenuHelper {
                 name = user.getName();
             }
             menu.setHeaderTitle(name);
-            MucDetailsContextMenuHelper.configureMucDetailsContextMenu(activity, menu, user.getConversation(), user);
+            MucDetailsContextMenuHelper.configureMucDetailsContextMenu(
+                    activity, menu, user.getConversation(), user);
         }
     }
 
-    public static void configureMucDetailsContextMenu(Activity activity, Menu menu, Conversation conversation, User user) {
+    public static void configureMucDetailsContextMenu(
+            Activity activity, Menu menu, Conversation conversation, User user) {
         final MucOptions mucOptions = conversation.getMucOptions();
-        final boolean advancedMode = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("advanced_muc_mode", false);
+        final boolean advancedMode =
+                PreferenceManager.getDefaultSharedPreferences(activity)
+                        .getBoolean("advanced_muc_mode", false);
         final boolean isGroupChat = mucOptions.isPrivateAndNonAnonymous();
         MenuItem sendPrivateMessage = menu.findItem(R.id.send_private_message);
         if (user != null && user.getRealJid() != null) {
@@ -68,21 +66,28 @@ public final class MucDetailsContextMenuHelper {
             MenuItem removeAdminPrivileges = menu.findItem(R.id.remove_admin_privileges);
             MenuItem removeFromRoom = menu.findItem(R.id.remove_from_room);
             MenuItem managePermissions = menu.findItem(R.id.manage_permissions);
-            removeFromRoom.setTitle(isGroupChat ? R.string.remove_from_room : R.string.remove_from_channel);
+            removeFromRoom.setTitle(
+                    isGroupChat ? R.string.remove_from_room : R.string.remove_from_channel);
             MenuItem banFromConference = menu.findItem(R.id.ban_from_conference);
-            banFromConference.setTitle(isGroupChat ? R.string.ban_from_conference : R.string.ban_from_channel);
+            banFromConference.setTitle(
+                    isGroupChat ? R.string.ban_from_conference : R.string.ban_from_channel);
             MenuItem invite = menu.findItem(R.id.invite);
             startConversation.setVisible(true);
             final Contact contact = user.getContact();
             final User self = conversation.getMucOptions().getSelf();
-            if ((contact != null && contact.showInRoster()) || mucOptions.isPrivateAndNonAnonymous()) {
+            if ((contact != null && contact.showInRoster())
+                    || mucOptions.isPrivateAndNonAnonymous()) {
                 showContactDetails.setVisible(contact == null || !contact.isSelf());
             }
-            if ((activity instanceof ConferenceDetailsActivity || activity instanceof MucUsersActivity) && user.getRole() == MucOptions.Role.NONE) {
+            if ((activity instanceof ConferenceDetailsActivity
+                            || activity instanceof MucUsersActivity)
+                    && user.getRole() == MucOptions.Role.NONE) {
                 invite.setVisible(true);
             }
             boolean managePermissionsVisible = false;
-            if ((self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN) && self.getAffiliation().outranks(user.getAffiliation())) || self.getAffiliation() == MucOptions.Affiliation.OWNER) {
+            if ((self.getAffiliation().ranks(MucOptions.Affiliation.ADMIN)
+                            && self.getAffiliation().outranks(user.getAffiliation()))
+                    || self.getAffiliation() == MucOptions.Affiliation.OWNER) {
                 if (advancedMode) {
                     if (!user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) {
                         managePermissionsVisible = true;
@@ -102,16 +107,20 @@ public final class MucDetailsContextMenuHelper {
                 }
             }
             if (self.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
-                if (isGroupChat || advancedMode || user.getAffiliation() == MucOptions.Affiliation.OWNER) {
+                if (isGroupChat
+                        || advancedMode
+                        || user.getAffiliation() == MucOptions.Affiliation.OWNER) {
                     if (!user.getAffiliation().ranks(MucOptions.Affiliation.OWNER)) {
                         managePermissionsVisible = true;
                         giveOwnerPrivileges.setVisible(true);
-                    } else if (user.getAffiliation() == MucOptions.Affiliation.OWNER){
+                    } else if (user.getAffiliation() == MucOptions.Affiliation.OWNER) {
                         managePermissionsVisible = true;
                         removeOwnerPrivileges.setVisible(true);
                     }
                 }
-                if (!isGroupChat || advancedMode || user.getAffiliation() == MucOptions.Affiliation.ADMIN) {
+                if (!isGroupChat
+                        || advancedMode
+                        || user.getAffiliation() == MucOptions.Affiliation.ADMIN) {
                     if (!user.getAffiliation().ranks(MucOptions.Affiliation.ADMIN)) {
                         managePermissionsVisible = true;
                         giveAdminPrivileges.setVisible(true);
@@ -122,10 +131,16 @@ public final class MucDetailsContextMenuHelper {
                 }
             }
             managePermissions.setVisible(managePermissionsVisible);
-            sendPrivateMessage.setVisible(!isGroupChat && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR));
+            sendPrivateMessage.setVisible(
+                    !isGroupChat
+                            && mucOptions.allowPm()
+                            && user.getRole().ranks(MucOptions.Role.VISITOR));
         } else {
             sendPrivateMessage.setVisible(true);
-            sendPrivateMessage.setEnabled(user != null && mucOptions.allowPm() && user.getRole().ranks(MucOptions.Role.VISITOR));
+            sendPrivateMessage.setEnabled(
+                    user != null
+                            && mucOptions.allowPm()
+                            && user.getRole().ranks(MucOptions.Role.VISITOR));
         }
     }
 
@@ -133,15 +148,20 @@ public final class MucDetailsContextMenuHelper {
         return onContextItemSelected(item, user, activity, null);
     }
 
-    public static boolean onContextItemSelected(MenuItem item, User user, XmppActivity activity, final String fingerprint) {
+    public static boolean onContextItemSelected(
+            MenuItem item, User user, XmppActivity activity, final String fingerprint) {
         final Conversation conversation = user.getConversation();
-        final XmppConnectionService.OnAffiliationChanged onAffiliationChanged = activity instanceof XmppConnectionService.OnAffiliationChanged ? (XmppConnectionService.OnAffiliationChanged) activity : null;
+        final XmppConnectionService.OnAffiliationChanged onAffiliationChanged =
+                activity instanceof XmppConnectionService.OnAffiliationChanged
+                        ? (XmppConnectionService.OnAffiliationChanged) activity
+                        : null;
         Jid jid = user.getRealJid();
         switch (item.getItemId()) {
             case R.id.action_contact_details:
                 final Jid realJid = user.getRealJid();
                 final Account account = conversation.getAccount();
-                final Contact contact = realJid == null ? null : account.getRoster().getContact(realJid);
+                final Contact contact =
+                        realJid == null ? null : account.getRoster().getContact(realJid);
                 if (contact != null) {
                     activity.switchToContactDetails(contact, fingerprint);
                 }
@@ -150,26 +170,32 @@ public final class MucDetailsContextMenuHelper {
                 startConversation(user, activity);
                 return true;
             case R.id.give_admin_privileges:
-                activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.ADMIN, onAffiliationChanged);
+                activity.xmppConnectionService.changeAffiliationInConference(
+                        conversation, jid, MucOptions.Affiliation.ADMIN, onAffiliationChanged);
                 return true;
             case R.id.give_membership:
             case R.id.remove_admin_privileges:
             case R.id.revoke_owner_privileges:
-                activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.MEMBER, onAffiliationChanged);
+                activity.xmppConnectionService.changeAffiliationInConference(
+                        conversation, jid, MucOptions.Affiliation.MEMBER, onAffiliationChanged);
                 return true;
             case R.id.give_owner_privileges:
-                activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.OWNER, onAffiliationChanged);
+                activity.xmppConnectionService.changeAffiliationInConference(
+                        conversation, jid, MucOptions.Affiliation.OWNER, onAffiliationChanged);
                 return true;
             case R.id.remove_membership:
-                activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.NONE, onAffiliationChanged);
+                activity.xmppConnectionService.changeAffiliationInConference(
+                        conversation, jid, MucOptions.Affiliation.NONE, onAffiliationChanged);
                 return true;
             case R.id.remove_from_room:
                 removeFromRoom(user, activity, onAffiliationChanged);
                 return true;
             case R.id.ban_from_conference:
-                activity.xmppConnectionService.changeAffiliationInConference(conversation, jid, MucOptions.Affiliation.OUTCAST, onAffiliationChanged);
+                activity.xmppConnectionService.changeAffiliationInConference(
+                        conversation, jid, MucOptions.Affiliation.OUTCAST, onAffiliationChanged);
                 if (user.getRole() != MucOptions.Role.NONE) {
-                    activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE);
+                    activity.xmppConnectionService.changeRoleInConference(
+                            conversation, user.getName(), MucOptions.Role.NONE);
                 }
                 return true;
             case R.id.send_private_message:
@@ -195,38 +221,61 @@ public final class MucDetailsContextMenuHelper {
         }
     }
 
-    private static void removeFromRoom(final User user, XmppActivity activity, XmppConnectionService.OnAffiliationChanged onAffiliationChanged) {
+    private static void removeFromRoom(
+            final User user,
+            XmppActivity activity,
+            XmppConnectionService.OnAffiliationChanged onAffiliationChanged) {
         final Conversation conversation = user.getConversation();
         if (conversation.getMucOptions().membersOnly()) {
-            activity.xmppConnectionService.changeAffiliationInConference(conversation, user.getRealJid(), MucOptions.Affiliation.NONE, onAffiliationChanged);
+            activity.xmppConnectionService.changeAffiliationInConference(
+                    conversation,
+                    user.getRealJid(),
+                    MucOptions.Affiliation.NONE,
+                    onAffiliationChanged);
             if (user.getRole() != MucOptions.Role.NONE) {
-                activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE);
+                activity.xmppConnectionService.changeRoleInConference(
+                        conversation, user.getName(), MucOptions.Role.NONE);
             }
         } else {
             final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
             builder.setTitle(R.string.ban_from_conference);
             String jid = user.getRealJid().asBareJid().toString();
-            SpannableString message = new SpannableString(activity.getString(R.string.removing_from_public_conference, jid));
+            SpannableString message =
+                    new SpannableString(
+                            activity.getString(R.string.removing_from_public_conference, jid));
             int start = message.toString().indexOf(jid);
             if (start >= 0) {
-                message.setSpan(new TypefaceSpan("monospace"), start, start + jid.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                message.setSpan(
+                        new TypefaceSpan("monospace"),
+                        start,
+                        start + jid.length(),
+                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             }
             builder.setMessage(message);
             builder.setNegativeButton(R.string.cancel, null);
-            builder.setPositiveButton(R.string.ban_now, (dialog, which) -> {
-                activity.xmppConnectionService.changeAffiliationInConference(conversation, user.getRealJid(), MucOptions.Affiliation.OUTCAST, onAffiliationChanged);
-                if (user.getRole() != MucOptions.Role.NONE) {
-                    activity.xmppConnectionService.changeRoleInConference(conversation, user.getName(), MucOptions.Role.NONE);
-                }
-            });
+            builder.setPositiveButton(
+                    R.string.ban_now,
+                    (dialog, which) -> {
+                        activity.xmppConnectionService.changeAffiliationInConference(
+                                conversation,
+                                user.getRealJid(),
+                                MucOptions.Affiliation.OUTCAST,
+                                onAffiliationChanged);
+                        if (user.getRole() != MucOptions.Role.NONE) {
+                            activity.xmppConnectionService.changeRoleInConference(
+                                    conversation, user.getName(), MucOptions.Role.NONE);
+                        }
+                    });
             builder.create().show();
         }
     }
 
     private static void startConversation(User user, XmppActivity activity) {
         if (user.getRealJid() != null) {
-            Conversation newConversation = activity.xmppConnectionService.findOrCreateConversation(user.getAccount(), user.getRealJid().asBareJid(), false, true);
+            Conversation newConversation =
+                    activity.xmppConnectionService.findOrCreateConversation(
+                            user.getAccount(), user.getRealJid().asBareJid(), false, true);
             activity.switchToConversation(newConversation);
         }
     }
-}
+}

src/main/java/eu/siacs/conversations/ui/widget/ScannerView.java 🔗

@@ -28,22 +28,18 @@ import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.view.View;
-
 import androidx.core.content.ContextCompat;
-
 import com.google.zxing.ResultPoint;
-
+import eu.siacs.conversations.R;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-import eu.siacs.conversations.R;
-
 /**
  * @author Andreas Schildbach
  */
 public class ScannerView extends View {
-    private static final long LASER_ANIMATION_DELAY_MS = 100l;
+    private static final long LASER_ANIMATION_DELAY_MS = 100L;
     private static final int DOT_OPACITY = 0xa0;
     private static final int DOT_TTL_MS = 500;
 
@@ -81,8 +77,12 @@ public class ScannerView extends View {
         dotPaint.setAntiAlias(true);
     }
 
-    public void setFraming(final Rect frame, final RectF framePreview, final int displayRotation,
-            final int cameraRotation, final boolean cameraFlip) {
+    public void setFraming(
+            final Rect frame,
+            final RectF framePreview,
+            final int displayRotation,
+            final int cameraRotation,
+            final boolean cameraFlip) {
         this.frame = frame;
         matrix.setRectToRect(framePreview, new RectF(frame), ScaleToFit.FILL);
         matrix.postRotate(-displayRotation, frame.exactCenterX(), frame.exactCenterY());
@@ -99,15 +99,14 @@ public class ScannerView extends View {
     }
 
     public void addDot(final ResultPoint dot) {
-        dots.put(new float[] { dot.getX(), dot.getY() }, System.currentTimeMillis());
+        dots.put(new float[] {dot.getX(), dot.getY()}, System.currentTimeMillis());
 
         invalidate();
     }
 
     @Override
     public void onDraw(final Canvas canvas) {
-        if (frame == null)
-            return;
+        if (frame == null) return;
 
         final long now = System.currentTimeMillis();
 
@@ -142,7 +141,8 @@ public class ScannerView extends View {
         canvas.drawRect(frame, laserPaint);
 
         // draw points
-        for (final Iterator<Map.Entry<float[], Long>> i = dots.entrySet().iterator(); i.hasNext();) {
+        for (final Iterator<Map.Entry<float[], Long>> i = dots.entrySet().iterator();
+                i.hasNext(); ) {
             final Map.Entry<float[], Long> entry = i.next();
             final long age = now - entry.getValue();
             if (age < DOT_TTL_MS) {

src/main/java/eu/siacs/conversations/utils/CursorUtils.java 🔗

@@ -9,8 +9,7 @@ public class CursorUtils {
 
     public static void upgradeCursorWindowSize(final Cursor cursor) {
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
-            if (cursor instanceof AbstractWindowedCursor) {
-                final AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
+            if (cursor instanceof AbstractWindowedCursor windowedCursor) {
                 windowedCursor.setWindow(new CursorWindow("4M", 4 * 1024 * 1024));
             }
             if (cursor instanceof SQLiteCursor) {
@@ -18,5 +17,4 @@ public class CursorUtils {
             }
         }
     }
-
 }

src/main/java/eu/siacs/conversations/utils/GeoHelper.java 🔗

@@ -12,8 +12,8 @@ import eu.siacs.conversations.entities.Conversational;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.ui.ShareLocationActivity;
 import eu.siacs.conversations.ui.ShowLocationActivity;
-import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.regex.Matcher;
 import org.osmdroid.util.GeoPoint;
@@ -160,11 +160,8 @@ public class GeoHelper {
 
     private static String getLabel(Context context, Message message) {
         if (message.getStatus() == Message.STATUS_RECEIVED) {
-            try {
-                return URLEncoder.encode(UIHelper.getMessageDisplayName(message), "UTF-8");
-            } catch (UnsupportedEncodingException e) {
-                throw new AssertionError(e);
-            }
+            return URLEncoder.encode(
+                    UIHelper.getMessageDisplayName(message), StandardCharsets.UTF_8);
         } else {
             return context.getString(R.string.me);
         }

src/main/java/eu/siacs/conversations/utils/ImStyleParser.java 🔗

@@ -35,11 +35,11 @@ import java.util.List;
 
 public class ImStyleParser {
 
-    private final static List<Character> KEYWORDS = Arrays.asList('*', '_', '~', '`');
-    private final static List<Character> NO_SUB_PARSING_KEYWORDS = Arrays.asList('`');
-    private final static List<Character> BLOCK_KEYWORDS = Arrays.asList('`');
-    private final static boolean ALLOW_EMPTY = false;
-    private final static boolean PARSE_HIGHER_ORDER_END = true;
+    private static final List<Character> KEYWORDS = Arrays.asList('*', '_', '~', '`');
+    private static final List<Character> NO_SUB_PARSING_KEYWORDS = List.of('`');
+    private static final List<Character> BLOCK_KEYWORDS = List.of('`');
+    private static final boolean ALLOW_EMPTY = false;
+    private static final boolean PARSE_HIGHER_ORDER_END = true;
 
     public static List<Style> parse(CharSequence text) {
         return parse(text, 0, text.length() - 1);
@@ -49,7 +49,9 @@ public class ImStyleParser {
         List<Style> styles = new ArrayList<>();
         for (int i = start; i <= end; ++i) {
             char c = text.charAt(i);
-            if (KEYWORDS.contains(c) && precededByWhiteSpace(text, i, start) && !followedByWhitespace(text, i, end)) {
+            if (KEYWORDS.contains(c)
+                    && precededByWhiteSpace(text, i, start)
+                    && !followedByWhitespace(text, i, end)) {
                 if (BLOCK_KEYWORDS.contains(c) && isCharRepeatedTwoTimes(text, c, i + 1, end)) {
                     int to = seekEndBlock(text, c, i + 3, end);
                     if (to != -1 && (to != i + 5 || ALLOW_EMPTY)) {
@@ -91,7 +93,8 @@ public class ImStyleParser {
                 if (!PARSE_HIGHER_ORDER_END || followedByWhitespace(text, i, end)) {
                     return i;
                 } else {
-                    int higherOrder = seekHigherOrderEndWithoutNewBeginning(text, needle, i + 1, end);
+                    int higherOrder =
+                            seekHigherOrderEndWithoutNewBeginning(text, needle, i + 1, end);
                     if (higherOrder != -1) {
                         return higherOrder;
                     }
@@ -104,12 +107,17 @@ public class ImStyleParser {
         return -1;
     }
 
-    private static int seekHigherOrderEndWithoutNewBeginning(CharSequence text, char needle, int start, int end) {
+    private static int seekHigherOrderEndWithoutNewBeginning(
+            CharSequence text, char needle, int start, int end) {
         for (int i = start; i <= end; ++i) {
             char c = text.charAt(i);
-            if (c == needle && precededByWhiteSpace(text, i, start) && !followedByWhitespace(text, i, end)) {
+            if (c == needle
+                    && precededByWhiteSpace(text, i, start)
+                    && !followedByWhitespace(text, i, end)) {
                 return -1; // new beginning
-            } else if (c == needle && !Character.isWhitespace(text.charAt(i - 1)) && followedByWhitespace(text, i, end)) {
+            } else if (c == needle
+                    && !Character.isWhitespace(text.charAt(i - 1))
+                    && followedByWhitespace(text, i, end)) {
                 return i;
             } else if (c == '\n') {
                 return -1;

src/main/java/eu/siacs/conversations/utils/MimeUtils.java 🔗

@@ -145,6 +145,11 @@ public final class MimeUtils {
         add("application/vnd.sun.xml.writer.global", "sxg");
         add("application/vnd.sun.xml.writer.template", "stw");
         add("application/vnd.visio", "vsd");
+        // https://www.iana.org/assignments/media-types/application/vnd.tcpdump.pcap
+        add("application/vnd.tcpdump.pcap", "pcap");
+        add("application/vnd.tcpdump.pcap", "cap");
+        add("application/vnd.tcpdump.pcap", "dmp");
+        add("application/x-pcapng", "pcapng");
         add("application/x-7z-compressed", "7z");
         add("application/x-abiword", "abw");
         add("application/x-apple-diskimage", "dmg");
@@ -425,8 +430,7 @@ public final class MimeUtils {
     }
 
     // mime types that are more reliant by path
-    private static final Collection<String> PATH_PRECEDENCE_MIME_TYPE =
-            Arrays.asList("audio/x-m4b");
+    private static final Collection<String> PATH_PRECEDENCE_MIME_TYPE = List.of("audio/x-m4b");
 
     private static void add(String mimeType, String extension) {
         // If we have an existing x -> y mapping, we do not want to

src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java 🔗

@@ -67,8 +67,7 @@ public class Avatar {
 
     @Override
     public boolean equals(Object object) {
-        if (object != null && object instanceof Avatar) {
-            Avatar other = (Avatar) object;
+        if (object != null && object instanceof Avatar other) {
             return other.getFilename().equals(this.getFilename());
         } else {
             return false;

src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java 🔗

@@ -42,10 +42,7 @@ public abstract class Stanza extends StreamElement {
     public boolean isInvalid() {
         final var to = getTo();
         final var from = getFrom();
-        if (to instanceof Jid.Invalid || from instanceof Jid.Invalid) {
-            return true;
-        }
-        return false;
+        return to instanceof Jid.Invalid || from instanceof Jid.Invalid;
     }
 
     public boolean fromServer(final Account account) {

src/main/res/drawable/ic_help_center_48dp.xml 🔗

@@ -1,5 +1,13 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="48dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="48dp">
-      
-    <path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM12.01,18c-0.7,0 -1.26,-0.56 -1.26,-1.26c0,-0.71 0.56,-1.25 1.26,-1.25c0.71,0 1.25,0.54 1.25,1.25C13.25,17.43 12.72,18 12.01,18zM15.02,10.6c-0.76,1.11 -1.48,1.46 -1.87,2.17c-0.16,0.29 -0.22,0.48 -0.22,1.41h-1.82c0,-0.49 -0.08,-1.29 0.31,-1.98c0.49,-0.87 1.42,-1.39 1.96,-2.16c0.57,-0.81 0.25,-2.33 -1.37,-2.33c-1.06,0 -1.58,0.8 -1.8,1.48L8.56,8.49C9.01,7.15 10.22,6 11.99,6c1.48,0 2.49,0.67 3.01,1.52C15.44,8.24 15.7,9.59 15.02,10.6z"/>
-    
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:autoMirrored="true"
+    android:tint="?colorControlNormal"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM12.01,18c-0.7,0 -1.26,-0.56 -1.26,-1.26c0,-0.71 0.56,-1.25 1.26,-1.25c0.71,0 1.25,0.54 1.25,1.25C13.25,17.43 12.72,18 12.01,18zM15.02,10.6c-0.76,1.11 -1.48,1.46 -1.87,2.17c-0.16,0.29 -0.22,0.48 -0.22,1.41h-1.82c0,-0.49 -0.08,-1.29 0.31,-1.98c0.49,-0.87 1.42,-1.39 1.96,-2.16c0.57,-0.81 0.25,-2.33 -1.37,-2.33c-1.06,0 -1.58,0.8 -1.8,1.48L8.56,8.49C9.01,7.15 10.22,6 11.99,6c1.48,0 2.49,0.67 3.01,1.52C15.44,8.24 15.7,9.59 15.02,10.6z" />
+
 </vector>

src/main/res/drawable/ic_lan_24dp.xml 🔗

@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:tint="?colorControlNormal"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M13,22h8v-7h-3v-4h-5V9h3V2H8v7h3v2H6v4H3v7h8v-7H8v-2h8v2h-3V22zM10,7V4h4v3H10zM9,17v3H5v-3H9zM19,17v3h-4v-3H19z" />
+
+</vector>