apply spotless formatting

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/persistance/FileBackend.java                   |   7 
src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java |   4 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java            | 803 
3 files changed, 464 insertions(+), 350 deletions(-)

Detailed changes

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

@@ -26,16 +26,13 @@ import android.util.Base64OutputStream;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.LruCache;
-
 import androidx.annotation.RequiresApi;
 import androidx.annotation.StringRes;
 import androidx.core.content.FileProvider;
 import androidx.exifinterface.media.ExifInterface;
-
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.DownloadableFile;
@@ -49,7 +46,6 @@ import eu.siacs.conversations.utils.FileUtils;
 import eu.siacs.conversations.utils.FileWriterException;
 import eu.siacs.conversations.utils.MimeUtils;
 import eu.siacs.conversations.xmpp.pep.Avatar;
-
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.File;
@@ -140,7 +136,8 @@ public class FileBackend {
                     if (dimensions.getMin() > 720) {
                         Log.d(
                                 Config.LOGTAG,
-                                "do not consider video file with min width larger than 720 for size check");
+                                "do not consider video file with min width larger than 720 for size"
+                                        + " check");
                         continue;
                     }
                 } catch (final IOException | NotAVideoFile e) {

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

@@ -5,12 +5,9 @@ import android.content.SharedPreferences;
 import android.net.Uri;
 import android.preference.PreferenceManager;
 import android.util.Log;
-
 import androidx.annotation.NonNull;
-
 import com.otaliastudios.transcoder.Transcoder;
 import com.otaliastudios.transcoder.TranscoderListener;
-
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.PgpEngine;
@@ -20,7 +17,6 @@ import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.ui.UiCallback;
 import eu.siacs.conversations.utils.MimeUtils;
 import eu.siacs.conversations.utils.TranscoderStrategies;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.Objects;

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

@@ -45,55 +45,18 @@ import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.LruCache;
 import android.util.Pair;
-
 import androidx.annotation.BoolRes;
 import androidx.annotation.IntegerRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.app.RemoteInput;
 import androidx.core.content.ContextCompat;
-
 import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-
-import org.conscrypt.Conscrypt;
-import org.jxmpp.stringprep.libidn.LibIdnXmppStringprep;
-import org.openintents.openpgp.IOpenPgpService2;
-import org.openintents.openpgp.util.OpenPgpApi;
-import org.openintents.openpgp.util.OpenPgpServiceConnection;
-
-import java.io.File;
-import java.security.Security;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-
 import eu.siacs.conversations.AppSettings;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
@@ -175,7 +138,39 @@ import eu.siacs.conversations.xmpp.mam.MamReference;
 import eu.siacs.conversations.xmpp.pep.Avatar;
 import eu.siacs.conversations.xmpp.pep.PublishOptions;
 import im.conversations.android.xmpp.model.stanza.Iq;
+import java.io.File;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import me.leolin.shortcutbadger.ShortcutBadger;
+import org.conscrypt.Conscrypt;
+import org.jxmpp.stringprep.libidn.LibIdnXmppStringprep;
+import org.openintents.openpgp.IOpenPgpService2;
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.openintents.openpgp.util.OpenPgpServiceConnection;
 
 public class XmppConnectionService extends Service {
 
@@ -183,7 +178,8 @@ public class XmppConnectionService extends Service {
     public static final String ACTION_MARK_AS_READ = "mark_as_read";
     public static final String ACTION_SNOOZE = "snooze";
     public static final String ACTION_CLEAR_MESSAGE_NOTIFICATION = "clear_message_notification";
-    public static final String ACTION_CLEAR_MISSED_CALL_NOTIFICATION = "clear_missed_call_notification";
+    public static final String ACTION_CLEAR_MISSED_CALL_NOTIFICATION =
+            "clear_missed_call_notification";
     public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = "dismiss_error";
     public static final String ACTION_TRY_AGAIN = "try_again";
 
@@ -196,22 +192,30 @@ public class XmppConnectionService extends Service {
     public static final String ACTION_DISMISS_CALL = "dismiss_call";
     public static final String ACTION_END_CALL = "end_call";
     public static final String ACTION_PROVISION_ACCOUNT = "provision_account";
-    public static final String ACTION_CALL_INTEGRATION_SERVICE_STARTED = "call_integration_service_started";
-    private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
-    public static final String ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS = "eu.siacs.conversations.UNIFIED_PUSH_RENEW";
+    public static final String ACTION_CALL_INTEGRATION_SERVICE_STARTED =
+            "call_integration_service_started";
+    private static final String ACTION_POST_CONNECTIVITY_CHANGE =
+            "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
+    public static final String ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS =
+            "eu.siacs.conversations.UNIFIED_PUSH_RENEW";
     public static final String ACTION_QUICK_LOG = "eu.siacs.conversations.QUICK_LOG";
 
     private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
 
     public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1);
-    private final static Executor FILE_OBSERVER_EXECUTOR = Executors.newSingleThreadExecutor();
-    public final static Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor();
-
-    private final ScheduledExecutorService internalPingExecutor = Executors.newSingleThreadScheduledExecutor();
-    private final static SerialSingleThreadExecutor VIDEO_COMPRESSION_EXECUTOR = new SerialSingleThreadExecutor("VideoCompression");
-    private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter");
-    private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader");
-    private final SerialSingleThreadExecutor mNotificationExecutor = new SerialSingleThreadExecutor("NotificationExecutor");
+    private static final Executor FILE_OBSERVER_EXECUTOR = Executors.newSingleThreadExecutor();
+    public static final Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor();
+
+    private final ScheduledExecutorService internalPingExecutor =
+            Executors.newSingleThreadScheduledExecutor();
+    private static final SerialSingleThreadExecutor VIDEO_COMPRESSION_EXECUTOR =
+            new SerialSingleThreadExecutor("VideoCompression");
+    private final SerialSingleThreadExecutor mDatabaseWriterExecutor =
+            new SerialSingleThreadExecutor("DatabaseWriter");
+    private final SerialSingleThreadExecutor mDatabaseReaderExecutor =
+            new SerialSingleThreadExecutor("DatabaseReader");
+    private final SerialSingleThreadExecutor mNotificationExecutor =
+            new SerialSingleThreadExecutor("NotificationExecutor");
     private final ReplacingTaskManager mRosterSyncTaskManager = new ReplacingTaskManager();
     private final IBinder mBinder = new XmppConnectionBinder();
     private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
@@ -219,17 +223,19 @@ public class XmppConnectionService extends Service {
     private final Set<String> mInProgressAvatarFetches = new HashSet<>();
     private final Set<String> mOmittedPepAvatarFetches = new HashSet<>();
     private final HashSet<Jid> mLowPingTimeoutMode = new HashSet<>();
-    private final Consumer<Iq> mDefaultIqHandler = (packet) -> {
-        if (packet.getType() != Iq.Type.RESULT) {
-            final var error = packet.getError();
-            String text = error != null ? error.findChildContent("text") : null;
-            if (text != null) {
-                Log.d(Config.LOGTAG, "received iq error: " + text);
-            }
-        }
-    };
+    private final Consumer<Iq> mDefaultIqHandler =
+            (packet) -> {
+                if (packet.getType() != Iq.Type.RESULT) {
+                    final var error = packet.getError();
+                    String text = error != null ? error.findChildContent("text") : null;
+                    if (text != null) {
+                        Log.d(Config.LOGTAG, "received iq error: " + text);
+                    }
+                }
+            };
     public DatabaseBackend databaseBackend;
-    private final ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor("ContactMerger");
+    private final ReplacingSerialSingleThreadExecutor mContactMergerExecutor =
+            new ReplacingSerialSingleThreadExecutor("ContactMerger");
     private long mLastActivity = 0;
 
     private final AppSettings appSettings = new AppSettings(this);
@@ -237,204 +243,253 @@ public class XmppConnectionService extends Service {
     private MemorizingTrustManager mMemorizingTrustManager;
     private final NotificationService mNotificationService = new NotificationService(this);
     private final UnifiedPushBroker unifiedPushBroker = new UnifiedPushBroker(this);
-    private final ChannelDiscoveryService mChannelDiscoveryService = new ChannelDiscoveryService(this);
+    private final ChannelDiscoveryService mChannelDiscoveryService =
+            new ChannelDiscoveryService(this);
     private final ShortcutService mShortcutService = new ShortcutService(this);
     private final AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false);
     private final AtomicBoolean mOngoingVideoTranscoding = new AtomicBoolean(false);
     private final AtomicBoolean mForceDuringOnCreate = new AtomicBoolean(false);
     private final AtomicReference<OngoingCall> ongoingCall = new AtomicReference<>();
     private final MessageGenerator mMessageGenerator = new MessageGenerator(this);
-    public OnContactStatusChanged onContactStatusChanged = (contact, online) -> {
-        Conversation conversation = find(getConversations(), contact);
-        if (conversation != null) {
-            if (online) {
-                if (contact.getPresences().size() == 1) {
-                    sendUnsentMessages(conversation);
+    public OnContactStatusChanged onContactStatusChanged =
+            (contact, online) -> {
+                Conversation conversation = find(getConversations(), contact);
+                if (conversation != null) {
+                    if (online) {
+                        if (contact.getPresences().size() == 1) {
+                            sendUnsentMessages(conversation);
+                        }
+                    }
                 }
-            }
-        }
-    };
+            };
     private final PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
     private List<Account> accounts;
-    private final JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(this);
+    private final JingleConnectionManager mJingleConnectionManager =
+            new JingleConnectionManager(this);
     private final HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(this);
     private final AvatarService mAvatarService = new AvatarService(this);
     private final MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
     private final PushManagementService mPushManagementService = new PushManagementService(this);
-    private final QuickConversationsService mQuickConversationsService = new QuickConversationsService(this);
-    private final ConversationsFileObserver fileObserver = new ConversationsFileObserver(
-            Environment.getExternalStorageDirectory().getAbsolutePath()
-    ) {
-        @Override
-        public void onEvent(final int event, final File file) {
-            markFileDeleted(file);
-        }
-    };
-    private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
-
-        @Override
-        public boolean onMessageAcknowledged(final Account account, final Jid to, final String id) {
-            if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) {
-                final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX.length());
-                mJingleConnectionManager.updateProposedSessionDiscovered(
-                        account,
-                        to,
-                        sessionId,
-                        JingleConnectionManager.DeviceDiscoveryState.SEARCHING_ACKNOWLEDGED
-                );
-            }
-
+    private final QuickConversationsService mQuickConversationsService =
+            new QuickConversationsService(this);
+    private final ConversationsFileObserver fileObserver =
+            new ConversationsFileObserver(
+                    Environment.getExternalStorageDirectory().getAbsolutePath()) {
+                @Override
+                public void onEvent(final int event, final File file) {
+                    markFileDeleted(file);
+                }
+            };
+    private final OnMessageAcknowledged mOnMessageAcknowledgedListener =
+            new OnMessageAcknowledged() {
 
-            final Jid bare = to.asBareJid();
+                @Override
+                public boolean onMessageAcknowledged(
+                        final Account account, final Jid to, final String id) {
+                    if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) {
+                        final String sessionId =
+                                id.substring(
+                                        JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX
+                                                .length());
+                        mJingleConnectionManager.updateProposedSessionDiscovered(
+                                account,
+                                to,
+                                sessionId,
+                                JingleConnectionManager.DeviceDiscoveryState
+                                        .SEARCHING_ACKNOWLEDGED);
+                    }
 
-            for (final Conversation conversation : getConversations()) {
-                if (conversation.getAccount() == account && conversation.getJid().asBareJid().equals(bare)) {
-                    final Message message = conversation.findUnsentMessageWithUuid(id);
-                    if (message != null) {
-                        message.setStatus(Message.STATUS_SEND);
-                        message.setErrorMessage(null);
-                        databaseBackend.updateMessage(message, false);
-                        return true;
+                    final Jid bare = to.asBareJid();
+
+                    for (final Conversation conversation : getConversations()) {
+                        if (conversation.getAccount() == account
+                                && conversation.getJid().asBareJid().equals(bare)) {
+                            final Message message = conversation.findUnsentMessageWithUuid(id);
+                            if (message != null) {
+                                message.setStatus(Message.STATUS_SEND);
+                                message.setErrorMessage(null);
+                                databaseBackend.updateMessage(message, false);
+                                return true;
+                            }
+                        }
                     }
+                    return false;
                 }
-            }
-            return false;
-        }
-    };
+            };
 
     private boolean destroyed = false;
 
     private int unreadCount = -1;
 
-    //Ui callback listeners
-    private final Set<OnConversationUpdate> mOnConversationUpdates = Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>());
-    private final Set<OnShowErrorToast> mOnShowErrorToasts = Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>());
-    private final Set<OnAccountUpdate> mOnAccountUpdates = Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>());
-    private final Set<OnCaptchaRequested> mOnCaptchaRequested = Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>());
-    private final Set<OnRosterUpdate> mOnRosterUpdates = Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>());
-    private final Set<OnUpdateBlocklist> mOnUpdateBlocklist = Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>());
-    private final Set<OnMucRosterUpdate> mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>());
-    private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>());
-    private final Set<OnJingleRtpConnectionUpdate> onJingleRtpConnectionUpdate = Collections.newSetFromMap(new WeakHashMap<OnJingleRtpConnectionUpdate, Boolean>());
+    // Ui callback listeners
+    private final Set<OnConversationUpdate> mOnConversationUpdates =
+            Collections.newSetFromMap(new WeakHashMap<OnConversationUpdate, Boolean>());
+    private final Set<OnShowErrorToast> mOnShowErrorToasts =
+            Collections.newSetFromMap(new WeakHashMap<OnShowErrorToast, Boolean>());
+    private final Set<OnAccountUpdate> mOnAccountUpdates =
+            Collections.newSetFromMap(new WeakHashMap<OnAccountUpdate, Boolean>());
+    private final Set<OnCaptchaRequested> mOnCaptchaRequested =
+            Collections.newSetFromMap(new WeakHashMap<OnCaptchaRequested, Boolean>());
+    private final Set<OnRosterUpdate> mOnRosterUpdates =
+            Collections.newSetFromMap(new WeakHashMap<OnRosterUpdate, Boolean>());
+    private final Set<OnUpdateBlocklist> mOnUpdateBlocklist =
+            Collections.newSetFromMap(new WeakHashMap<OnUpdateBlocklist, Boolean>());
+    private final Set<OnMucRosterUpdate> mOnMucRosterUpdate =
+            Collections.newSetFromMap(new WeakHashMap<OnMucRosterUpdate, Boolean>());
+    private final Set<OnKeyStatusUpdated> mOnKeyStatusUpdated =
+            Collections.newSetFromMap(new WeakHashMap<OnKeyStatusUpdated, Boolean>());
+    private final Set<OnJingleRtpConnectionUpdate> onJingleRtpConnectionUpdate =
+            Collections.newSetFromMap(new WeakHashMap<OnJingleRtpConnectionUpdate, Boolean>());
 
     private final Object LISTENER_LOCK = new Object();
 
-
     public final Set<String> FILENAMES_TO_IGNORE_DELETION = new HashSet<>();
 
-
-
     private final AtomicLong mLastExpiryRun = new AtomicLong(0);
-    private final LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache = new LruCache<>(20);
-    private final OnStatusChanged statusListener = new OnStatusChanged() {
-
-        @Override
-        public void onStatusChanged(final Account account) {
-            XmppConnection connection = account.getXmppConnection();
-            updateAccountUi();
+    private final LruCache<Pair<String, String>, ServiceDiscoveryResult> discoCache =
+            new LruCache<>(20);
+    private final OnStatusChanged statusListener =
+            new OnStatusChanged() {
 
-            if (account.getStatus() == Account.State.ONLINE || account.getStatus().isError()) {
-                mQuickConversationsService.signalAccountStateChange();
-            }
+                @Override
+                public void onStatusChanged(final Account account) {
+                    XmppConnection connection = account.getXmppConnection();
+                    updateAccountUi();
 
-            if (account.getStatus() == Account.State.ONLINE) {
-                synchronized (mLowPingTimeoutMode) {
-                    if (mLowPingTimeoutMode.remove(account.getJid().asBareJid())) {
-                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": leaving low ping timeout mode");
+                    if (account.getStatus() == Account.State.ONLINE
+                            || account.getStatus().isError()) {
+                        mQuickConversationsService.signalAccountStateChange();
                     }
-                }
-                if (account.setShowErrorNotification(true)) {
-                    databaseBackend.updateAccount(account);
-                }
-                mMessageArchiveService.executePendingQueries(account);
-                if (connection != null && connection.getFeatures().csi()) {
-                    if (checkListeners()) {
-                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + " sending csi//inactive");
-                        connection.sendInactive();
-                    } else {
-                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + " sending csi//active");
-                        connection.sendActive();
-                    }
-                }
-                List<Conversation> conversations = getConversations();
-                for (Conversation conversation : conversations) {
-                    final boolean inProgressJoin;
-                    synchronized (account.inProgressConferenceJoins) {
-                        inProgressJoin = account.inProgressConferenceJoins.contains(conversation);
-                    }
-                    final boolean pendingJoin;
-                    synchronized (account.pendingConferenceJoins) {
-                        pendingJoin = account.pendingConferenceJoins.contains(conversation);
-                    }
-                    if (conversation.getAccount() == account
-                            && !pendingJoin
-                            && !inProgressJoin) {
-                        sendUnsentMessages(conversation);
-                    }
-                }
-                final List<Conversation> pendingLeaves;
-                synchronized (account.pendingConferenceLeaves) {
-                    pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves);
-                    account.pendingConferenceLeaves.clear();
 
-                }
-                for (Conversation conversation : pendingLeaves) {
-                    leaveMuc(conversation);
-                }
-                final List<Conversation> pendingJoins;
-                synchronized (account.pendingConferenceJoins) {
-                    pendingJoins = new ArrayList<>(account.pendingConferenceJoins);
-                    account.pendingConferenceJoins.clear();
-                }
-                for (Conversation conversation : pendingJoins) {
-                    joinMuc(conversation);
-                }
-                scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
-            } else if (account.getStatus() == Account.State.OFFLINE || account.getStatus() == Account.State.DISABLED || account.getStatus() == Account.State.LOGGED_OUT) {
-                resetSendingToWaiting(account);
-                if (account.isConnectionEnabled() && isInLowPingTimeoutMode(account)) {
-                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": went into offline state during low ping mode. reconnecting now");
-                    reconnectAccount(account, true, false);
-                } else {
-                    final int timeToReconnect = SECURE_RANDOM.nextInt(10) + 2;
-                    scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
-                }
-            } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
-                databaseBackend.updateAccount(account);
-                reconnectAccount(account, true, false);
-            } else if (account.getStatus() != Account.State.CONNECTING && account.getStatus() != Account.State.NO_INTERNET) {
-                resetSendingToWaiting(account);
-                if (connection != null && account.getStatus().isAttemptReconnect()) {
-                    final boolean aggressive = account.getStatus() == Account.State.SEE_OTHER_HOST
-                            || hasJingleRtpConnection(account);
-                    final int next = connection.getTimeToNextAttempt(aggressive);
-                    final boolean lowPingTimeoutMode = isInLowPingTimeoutMode(account);
-                    if (next <= 0) {
-                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. reconnecting now. lowPingTimeout=" + lowPingTimeoutMode);
+                    if (account.getStatus() == Account.State.ONLINE) {
+                        synchronized (mLowPingTimeoutMode) {
+                            if (mLowPingTimeoutMode.remove(account.getJid().asBareJid())) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        account.getJid().asBareJid()
+                                                + ": leaving low ping timeout mode");
+                            }
+                        }
+                        if (account.setShowErrorNotification(true)) {
+                            databaseBackend.updateAccount(account);
+                        }
+                        mMessageArchiveService.executePendingQueries(account);
+                        if (connection != null && connection.getFeatures().csi()) {
+                            if (checkListeners()) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        account.getJid().asBareJid() + " sending csi//inactive");
+                                connection.sendInactive();
+                            } else {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        account.getJid().asBareJid() + " sending csi//active");
+                                connection.sendActive();
+                            }
+                        }
+                        List<Conversation> conversations = getConversations();
+                        for (Conversation conversation : conversations) {
+                            final boolean inProgressJoin;
+                            synchronized (account.inProgressConferenceJoins) {
+                                inProgressJoin =
+                                        account.inProgressConferenceJoins.contains(conversation);
+                            }
+                            final boolean pendingJoin;
+                            synchronized (account.pendingConferenceJoins) {
+                                pendingJoin = account.pendingConferenceJoins.contains(conversation);
+                            }
+                            if (conversation.getAccount() == account
+                                    && !pendingJoin
+                                    && !inProgressJoin) {
+                                sendUnsentMessages(conversation);
+                            }
+                        }
+                        final List<Conversation> pendingLeaves;
+                        synchronized (account.pendingConferenceLeaves) {
+                            pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves);
+                            account.pendingConferenceLeaves.clear();
+                        }
+                        for (Conversation conversation : pendingLeaves) {
+                            leaveMuc(conversation);
+                        }
+                        final List<Conversation> pendingJoins;
+                        synchronized (account.pendingConferenceJoins) {
+                            pendingJoins = new ArrayList<>(account.pendingConferenceJoins);
+                            account.pendingConferenceJoins.clear();
+                        }
+                        for (Conversation conversation : pendingJoins) {
+                            joinMuc(conversation);
+                        }
+                        scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
+                    } else if (account.getStatus() == Account.State.OFFLINE
+                            || account.getStatus() == Account.State.DISABLED
+                            || account.getStatus() == Account.State.LOGGED_OUT) {
+                        resetSendingToWaiting(account);
+                        if (account.isConnectionEnabled() && isInLowPingTimeoutMode(account)) {
+                            Log.d(
+                                    Config.LOGTAG,
+                                    account.getJid().asBareJid()
+                                            + ": went into offline state during low ping mode."
+                                            + " reconnecting now");
+                            reconnectAccount(account, true, false);
+                        } else {
+                            final int timeToReconnect = SECURE_RANDOM.nextInt(10) + 2;
+                            scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
+                        }
+                    } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) {
+                        databaseBackend.updateAccount(account);
                         reconnectAccount(account, true, false);
-                    } else {
-                        final int attempt = connection.getAttempt() + 1;
-                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. try again in " + next + "s for the " + attempt + " time. lowPingTimeout=" + lowPingTimeoutMode+", aggressive="+aggressive);
-                        scheduleWakeUpCall(next, account.getUuid().hashCode());
-                        if (aggressive) {
-                            internalPingExecutor.schedule(
-                                    XmppConnectionService.this::manageAccountConnectionStatesInternal,
-                                    (next * 1000L) + 50,
-                                    TimeUnit.MILLISECONDS
-                            );
+                    } else if (account.getStatus() != Account.State.CONNECTING
+                            && account.getStatus() != Account.State.NO_INTERNET) {
+                        resetSendingToWaiting(account);
+                        if (connection != null && account.getStatus().isAttemptReconnect()) {
+                            final boolean aggressive =
+                                    account.getStatus() == Account.State.SEE_OTHER_HOST
+                                            || hasJingleRtpConnection(account);
+                            final int next = connection.getTimeToNextAttempt(aggressive);
+                            final boolean lowPingTimeoutMode = isInLowPingTimeoutMode(account);
+                            if (next <= 0) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        account.getJid().asBareJid()
+                                                + ": error connecting account. reconnecting now."
+                                                + " lowPingTimeout="
+                                                + lowPingTimeoutMode);
+                                reconnectAccount(account, true, false);
+                            } else {
+                                final int attempt = connection.getAttempt() + 1;
+                                Log.d(
+                                        Config.LOGTAG,
+                                        account.getJid().asBareJid()
+                                                + ": error connecting account. try again in "
+                                                + next
+                                                + "s for the "
+                                                + attempt
+                                                + " time. lowPingTimeout="
+                                                + lowPingTimeoutMode
+                                                + ", aggressive="
+                                                + aggressive);
+                                scheduleWakeUpCall(next, account.getUuid().hashCode());
+                                if (aggressive) {
+                                    internalPingExecutor.schedule(
+                                            XmppConnectionService.this
+                                                    ::manageAccountConnectionStatesInternal,
+                                            (next * 1000L) + 50,
+                                            TimeUnit.MILLISECONDS);
+                                }
+                            }
                         }
                     }
+                    getNotificationService().updateErrorNotification();
                 }
-            }
-            getNotificationService().updateErrorNotification();
-        }
-    };
+            };
     private OpenPgpServiceConnection pgpServiceConnection;
     private PgpEngine mPgpEngine = null;
     private WakeLock wakeLock;
     private LruCache<String, Bitmap> mBitmapCache;
     private final BroadcastReceiver mInternalEventReceiver = new InternalEventReceiver();
-    private final BroadcastReceiver mInternalRestrictedEventReceiver = new RestrictedEventReceiver(Arrays.asList(TorServiceUtils.ACTION_STATUS));
+    private final BroadcastReceiver mInternalRestrictedEventReceiver =
+            new RestrictedEventReceiver(Arrays.asList(TorServiceUtils.ACTION_STATUS));
     private final BroadcastReceiver mInternalScreenEventReceiver = new InternalEventReceiver();
 
     private static String generateFetchKey(Account account, final Avatar avatar) {
@@ -466,15 +521,16 @@ public class XmppConnectionService extends Service {
             return null;
         } else if (pgpServiceConnection != null && pgpServiceConnection.isBound()) {
             if (this.mPgpEngine == null) {
-                this.mPgpEngine = new PgpEngine(new OpenPgpApi(
-                        getApplicationContext(),
-                        pgpServiceConnection.getService()), this);
+                this.mPgpEngine =
+                        new PgpEngine(
+                                new OpenPgpApi(
+                                        getApplicationContext(), pgpServiceConnection.getService()),
+                                this);
             }
             return mPgpEngine;
         } else {
             return null;
         }
-
     }
 
     public OpenPgpApi getOpenPgpApi() {
@@ -499,7 +555,8 @@ public class XmppConnectionService extends Service {
         return this.mAvatarService;
     }
 
-    public void attachLocationToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
+    public void attachLocationToConversation(
+            final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
         int encryption = conversation.getNextEncryption();
         if (encryption == Message.ENCRYPTION_PGP) {
             encryption = Message.ENCRYPTION_DECRYPTED;
@@ -514,7 +571,11 @@ public class XmppConnectionService extends Service {
         }
     }
 
-    public void attachFileToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
+    public void attachFileToConversation(
+            final Conversation conversation,
+            final Uri uri,
+            final String type,
+            final UiCallback<Message> callback) {
         final Message message;
         if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
             message = new Message(conversation, "", Message.ENCRYPTION_DECRYPTED);
@@ -527,7 +588,8 @@ public class XmppConnectionService extends Service {
         }
         Log.d(Config.LOGTAG, "attachFile: type=" + message.getType());
         Log.d(Config.LOGTAG, "counterpart=" + message.getCounterpart());
-        final AttachFileToConversationRunnable runnable = new AttachFileToConversationRunnable(this, uri, type, message, callback);
+        final AttachFileToConversationRunnable runnable =
+                new AttachFileToConversationRunnable(this, uri, type, message, callback);
         if (runnable.isVideoMessage()) {
             VIDEO_COMPRESSION_EXECUTOR.execute(runnable);
         } else {
@@ -535,7 +597,11 @@ public class XmppConnectionService extends Service {
         }
     }
 
-    public void attachImageToConversation(final Conversation conversation, final Uri uri,  final String type, final UiCallback<Message> callback) {
+    public void attachImageToConversation(
+            final Conversation conversation,
+            final Uri uri,
+            final String type,
+            final UiCallback<Message> callback) {
         final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(this, uri, type);
         final String compressPictures = getCompressPicturesPreference();
 
@@ -543,7 +609,10 @@ public class XmppConnectionService extends Service {
                 || ("auto".equals(compressPictures) && getFileBackend().useImageAsIs(uri))
                 || (mimeType != null && mimeType.endsWith("/gif"))
                 || getFileBackend().unusualBounds(uri)) {
-            Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": not compressing picture. sending as file");
+            Log.d(
+                    Config.LOGTAG,
+                    conversation.getAccount().getJid().asBareJid()
+                            + ": not compressing picture. sending as file");
             attachFileToConversation(conversation, uri, mimeType, callback);
             return;
         }
@@ -558,29 +627,33 @@ public class XmppConnectionService extends Service {
             message.setType(Message.TYPE_IMAGE);
         }
         Log.d(Config.LOGTAG, "attachImage: type=" + message.getType());
-        FILE_ATTACHMENT_EXECUTOR.execute(() -> {
-            try {
-                getFileBackend().copyImageToPrivateStorage(message, uri);
-            } catch (FileBackend.ImageCompressionException e) {
-                Log.d(Config.LOGTAG, "unable to compress image. fall back to file transfer", e);
-                attachFileToConversation(conversation, uri, mimeType, callback);
-                return;
-            } catch (final FileBackend.FileCopyException e) {
-                callback.error(e.getResId(), message);
-                return;
-            }
-            if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
-                final PgpEngine pgpEngine = getPgpEngine();
-                if (pgpEngine != null) {
-                    pgpEngine.encrypt(message, callback);
-                } else if (callback != null) {
-                    callback.error(R.string.unable_to_connect_to_keychain, null);
-                }
-            } else {
-                sendMessage(message);
-                callback.success(message);
-            }
-        });
+        FILE_ATTACHMENT_EXECUTOR.execute(
+                () -> {
+                    try {
+                        getFileBackend().copyImageToPrivateStorage(message, uri);
+                    } catch (FileBackend.ImageCompressionException e) {
+                        Log.d(
+                                Config.LOGTAG,
+                                "unable to compress image. fall back to file transfer",
+                                e);
+                        attachFileToConversation(conversation, uri, mimeType, callback);
+                        return;
+                    } catch (final FileBackend.FileCopyException e) {
+                        callback.error(e.getResId(), message);
+                        return;
+                    }
+                    if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
+                        final PgpEngine pgpEngine = getPgpEngine();
+                        if (pgpEngine != null) {
+                            pgpEngine.encrypt(message, callback);
+                        } else if (callback != null) {
+                            callback.error(R.string.unable_to_connect_to_keychain, null);
+                        }
+                    } else {
+                        sendMessage(message);
+                        callback.success(message);
+                    }
+                });
     }
 
     public Conversation find(Bookmark bookmark) {
@@ -596,16 +669,26 @@ public class XmppConnectionService extends Service {
         return c != null && c.getMode() == Conversational.MODE_MULTI;
     }
 
-    public void search(final List<String> term, final String uuid, final OnSearchResultsAvailable onSearchResultsAvailable) {
+    public void search(
+            final List<String> term,
+            final String uuid,
+            final OnSearchResultsAvailable onSearchResultsAvailable) {
         MessageSearchTask.search(this, term, uuid, onSearchResultsAvailable);
     }
 
     @Override
     public int onStartCommand(final Intent intent, int flags, int startId) {
         final String action = Strings.nullToEmpty(intent == null ? null : intent.getAction());
-        final boolean needsForegroundService = intent != null && intent.getBooleanExtra(SystemEventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, false);
+        final boolean needsForegroundService =
+                intent != null
+                        && intent.getBooleanExtra(
+                                SystemEventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE, false);
         if (needsForegroundService) {
-            Log.d(Config.LOGTAG, "toggle forced foreground service after receiving event (action=" + action + ")");
+            Log.d(
+                    Config.LOGTAG,
+                    "toggle forced foreground service after receiving event (action="
+                            + action
+                            + ")");
             toggleForegroundService(true);
         }
         final String uuid = intent == null ? null : intent.getStringExtra("uuid");
@@ -628,75 +711,93 @@ public class XmppConnectionService extends Service {
                 logoutAndSave(true);
                 return START_NOT_STICKY;
             case ACTION_CLEAR_MESSAGE_NOTIFICATION:
-                mNotificationExecutor.execute(() -> {
-                    try {
-                        final Conversation c = findConversationByUuid(uuid);
-                        if (c != null) {
-                            mNotificationService.clearMessages(c);
-                        } else {
-                            mNotificationService.clearMessages();
-                        }
-                        restoredFromDatabaseLatch.await();
+                mNotificationExecutor.execute(
+                        () -> {
+                            try {
+                                final Conversation c = findConversationByUuid(uuid);
+                                if (c != null) {
+                                    mNotificationService.clearMessages(c);
+                                } else {
+                                    mNotificationService.clearMessages();
+                                }
+                                restoredFromDatabaseLatch.await();
 
-                    } catch (InterruptedException e) {
-                        Log.d(Config.LOGTAG, "unable to process clear message notification");
-                    }
-                });
+                            } catch (InterruptedException e) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        "unable to process clear message notification");
+                            }
+                        });
                 break;
             case ACTION_CLEAR_MISSED_CALL_NOTIFICATION:
-                mNotificationExecutor.execute(() -> {
-                    try {
-                        final Conversation c = findConversationByUuid(uuid);
-                        if (c != null) {
-                            mNotificationService.clearMissedCalls(c);
-                        } else {
-                            mNotificationService.clearMissedCalls();
-                        }
-                        restoredFromDatabaseLatch.await();
+                mNotificationExecutor.execute(
+                        () -> {
+                            try {
+                                final Conversation c = findConversationByUuid(uuid);
+                                if (c != null) {
+                                    mNotificationService.clearMissedCalls(c);
+                                } else {
+                                    mNotificationService.clearMissedCalls();
+                                }
+                                restoredFromDatabaseLatch.await();
 
-                    } catch (InterruptedException e) {
-                        Log.d(Config.LOGTAG, "unable to process clear missed call notification");
-                    }
-                });
+                            } catch (InterruptedException e) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        "unable to process clear missed call notification");
+                            }
+                        });
                 break;
-            case ACTION_DISMISS_CALL: {
-                if (intent == null) {
+            case ACTION_DISMISS_CALL:
+                {
+                    if (intent == null) {
+                        break;
+                    }
+                    final String sessionId =
+                            intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
+                    Log.d(
+                            Config.LOGTAG,
+                            "received intent to dismiss call with session id " + sessionId);
+                    mJingleConnectionManager.rejectRtpSession(sessionId);
                     break;
                 }
-                final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
-                Log.d(Config.LOGTAG, "received intent to dismiss call with session id " + sessionId);
-                mJingleConnectionManager.rejectRtpSession(sessionId);
-                break;
-            }
             case TorServiceUtils.ACTION_STATUS:
-                final String status = intent == null ? null : intent.getStringExtra(TorServiceUtils.EXTRA_STATUS);
-                //TODO port and host are in 'extras' - but this may not be a reliable source?
+                final String status =
+                        intent == null ? null : intent.getStringExtra(TorServiceUtils.EXTRA_STATUS);
+                // TODO port and host are in 'extras' - but this may not be a reliable source?
                 if ("ON".equals(status)) {
                     handleOrbotStartedEvent();
                     return START_STICKY;
                 }
                 break;
-            case ACTION_END_CALL: {
-                if (intent == null) {
-                    break;
-                }
-                final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
-                Log.d(Config.LOGTAG, "received intent to end call with session id " + sessionId);
-                mJingleConnectionManager.endRtpSession(sessionId);
-            }
-            break;
-            case ACTION_PROVISION_ACCOUNT: {
-                if (intent == null) {
-                    break;
+            case ACTION_END_CALL:
+                {
+                    if (intent == null) {
+                        break;
+                    }
+                    final String sessionId =
+                            intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
+                    Log.d(
+                            Config.LOGTAG,
+                            "received intent to end call with session id " + sessionId);
+                    mJingleConnectionManager.endRtpSession(sessionId);
                 }
-                final String address = intent.getStringExtra("address");
-                final String password = intent.getStringExtra("password");
-                if (QuickConversationsService.isQuicksy() || Strings.isNullOrEmpty(address) || Strings.isNullOrEmpty(password)) {
+                break;
+            case ACTION_PROVISION_ACCOUNT:
+                {
+                    if (intent == null) {
+                        break;
+                    }
+                    final String address = intent.getStringExtra("address");
+                    final String password = intent.getStringExtra("password");
+                    if (QuickConversationsService.isQuicksy()
+                            || Strings.isNullOrEmpty(address)
+                            || Strings.isNullOrEmpty(password)) {
+                        break;
+                    }
+                    provisionAccount(address, password);
                     break;
                 }
-                provisionAccount(address, password);
-                break;
-            }
             case ACTION_DISMISS_ERROR_NOTIFICATIONS:
                 dismissErrorNotifications();
                 break;
@@ -704,55 +805,75 @@ public class XmppConnectionService extends Service {
                 resetAllAttemptCounts(false, true);
                 break;
             case ACTION_REPLY_TO_CONVERSATION:
-                final Bundle remoteInput = intent == null ? null : RemoteInput.getResultsFromIntent(intent);
+                final Bundle remoteInput =
+                        intent == null ? null : RemoteInput.getResultsFromIntent(intent);
                 if (remoteInput == null) {
                     break;
                 }
                 final CharSequence body = remoteInput.getCharSequence("text_reply");
-                final boolean dismissNotification = intent.getBooleanExtra("dismiss_notification", false);
+                final boolean dismissNotification =
+                        intent.getBooleanExtra("dismiss_notification", false);
                 final String lastMessageUuid = intent.getStringExtra("last_message_uuid");
                 if (body == null || body.length() <= 0) {
                     break;
                 }
-                mNotificationExecutor.execute(() -> {
-                    try {
-                        restoredFromDatabaseLatch.await();
-                        final Conversation c = findConversationByUuid(uuid);
-                        if (c != null) {
-                            directReply(c, body.toString(), lastMessageUuid, dismissNotification);
-                        }
-                    } catch (InterruptedException e) {
-                        Log.d(Config.LOGTAG, "unable to process direct reply");
-                    }
-                });
+                mNotificationExecutor.execute(
+                        () -> {
+                            try {
+                                restoredFromDatabaseLatch.await();
+                                final Conversation c = findConversationByUuid(uuid);
+                                if (c != null) {
+                                    directReply(
+                                            c,
+                                            body.toString(),
+                                            lastMessageUuid,
+                                            dismissNotification);
+                                }
+                            } catch (InterruptedException e) {
+                                Log.d(Config.LOGTAG, "unable to process direct reply");
+                            }
+                        });
                 break;
             case ACTION_MARK_AS_READ:
-                mNotificationExecutor.execute(() -> {
-                    final Conversation c = findConversationByUuid(uuid);
-                    if (c == null) {
-                        Log.d(Config.LOGTAG, "received mark read intent for unknown conversation (" + uuid + ")");
-                        return;
-                    }
-                    try {
-                        restoredFromDatabaseLatch.await();
-                        sendReadMarker(c, null);
-                    } catch (InterruptedException e) {
-                        Log.d(Config.LOGTAG, "unable to process notification read marker for conversation " + c.getName());
-                    }
-
-                });
+                mNotificationExecutor.execute(
+                        () -> {
+                            final Conversation c = findConversationByUuid(uuid);
+                            if (c == null) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        "received mark read intent for unknown conversation ("
+                                                + uuid
+                                                + ")");
+                                return;
+                            }
+                            try {
+                                restoredFromDatabaseLatch.await();
+                                sendReadMarker(c, null);
+                            } catch (InterruptedException e) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        "unable to process notification read marker for"
+                                                + " conversation "
+                                                + c.getName());
+                            }
+                        });
                 break;
             case ACTION_SNOOZE:
-                mNotificationExecutor.execute(() -> {
-                    final Conversation c = findConversationByUuid(uuid);
-                    if (c == null) {
-                        Log.d(Config.LOGTAG, "received snooze intent for unknown conversation (" + uuid + ")");
-                        return;
-                    }
-                    c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000);
-                    mNotificationService.clearMessages(c);
-                    updateConversation(c);
-                });
+                mNotificationExecutor.execute(
+                        () -> {
+                            final Conversation c = findConversationByUuid(uuid);
+                            if (c == null) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        "received snooze intent for unknown conversation ("
+                                                + uuid
+                                                + ")");
+                                return;
+                            }
+                            c.setMutedTill(System.currentTimeMillis() + 30 * 60 * 1000);
+                            mNotificationService.clearMessages(c);
+                            updateConversation(c);
+                        });
             case AudioManager.RINGER_MODE_CHANGED_ACTION:
             case NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED:
                 if (dndOnSilentMode()) {