From 7945d46f26440034ce67cd84bb4c2bfe5c1b1d00 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 28 Nov 2024 08:14:30 +0100 Subject: [PATCH] apply spotless formatting --- .../persistance/FileBackend.java | 7 +- .../AttachFileToConversationRunnable.java | 4 - .../services/XmppConnectionService.java | 4098 +++++++++++------ 3 files changed, 2565 insertions(+), 1544 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index af53574c5005c121b4c4380a5fe94ff8af3be8b3..179d8b7e7c632ca26bb1e2a03f67200e9f2bb283 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/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) { diff --git a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java index 9c75badb2f420a85fa47529e3979bcb439ad045c..ef83f8df8b589ce1113595e0992f7260427f45f5 100644 --- a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java +++ b/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; diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index dce715e7a18f1a5480d0a3cc0a64780ddeb8ea48..1d324232514a83f026b47ef50c0ec158eb463871 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/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 conversations = new CopyOnWriteArrayList<>(); @@ -219,17 +223,19 @@ public class XmppConnectionService extends Service { private final Set mInProgressAvatarFetches = new HashSet<>(); private final Set mOmittedPepAvatarFetches = new HashSet<>(); private final HashSet mLowPingTimeoutMode = new HashSet<>(); - private final Consumer 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 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 = 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 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 mOnConversationUpdates = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnShowErrorToasts = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnAccountUpdates = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnCaptchaRequested = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnRosterUpdates = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnUpdateBlocklist = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnMucRosterUpdate = Collections.newSetFromMap(new WeakHashMap()); - private final Set mOnKeyStatusUpdated = Collections.newSetFromMap(new WeakHashMap()); - private final Set onJingleRtpConnectionUpdate = Collections.newSetFromMap(new WeakHashMap()); + // Ui callback listeners + private final Set mOnConversationUpdates = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnShowErrorToasts = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnAccountUpdates = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnCaptchaRequested = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnRosterUpdates = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnUpdateBlocklist = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnMucRosterUpdate = + Collections.newSetFromMap(new WeakHashMap()); + private final Set mOnKeyStatusUpdated = + Collections.newSetFromMap(new WeakHashMap()); + private final Set onJingleRtpConnectionUpdate = + Collections.newSetFromMap(new WeakHashMap()); private final Object LISTENER_LOCK = new Object(); - public final Set FILENAMES_TO_IGNORE_DELETION = new HashSet<>(); - - private final AtomicLong mLastExpiryRun = new AtomicLong(0); - private final LruCache, 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, 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 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 pendingLeaves; - synchronized (account.pendingConferenceLeaves) { - pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves); - account.pendingConferenceLeaves.clear(); - } - for (Conversation conversation : pendingLeaves) { - leaveMuc(conversation); - } - final List 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 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 pendingLeaves; + synchronized (account.pendingConferenceLeaves) { + pendingLeaves = new ArrayList<>(account.pendingConferenceLeaves); + account.pendingConferenceLeaves.clear(); + } + for (Conversation conversation : pendingLeaves) { + leaveMuc(conversation); + } + final List 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 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 callback) { + public void attachLocationToConversation( + final Conversation conversation, final Uri uri, final UiCallback 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 callback) { + public void attachFileToConversation( + final Conversation conversation, + final Uri uri, + final String type, + final UiCallback 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 callback) { + public void attachImageToConversation( + final Conversation conversation, + final Uri uri, + final String type, + final UiCallback 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 term, final String uuid, final OnSearchResultsAvailable onSearchResultsAvailable) { + public void search( + final List 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()) { @@ -779,12 +900,16 @@ public class XmppConnectionService extends Service { final Messenger messenger = intent.getParcelableExtra("messenger"); final UnifiedPushBroker.PushTargetMessenger pushTargetMessenger; if (messenger != null && application != null && instance != null) { - pushTargetMessenger = new UnifiedPushBroker.PushTargetMessenger(new UnifiedPushDatabase.PushTarget(application, instance),messenger); - Log.d(Config.LOGTAG,"found push target messenger"); + pushTargetMessenger = + new UnifiedPushBroker.PushTargetMessenger( + new UnifiedPushDatabase.PushTarget(application, instance), + messenger); + Log.d(Config.LOGTAG, "found push target messenger"); } else { pushTargetMessenger = null; } - final Optional transport = renewUnifiedPushEndpoints(pushTargetMessenger); + final Optional transport = + renewUnifiedPushEndpoints(pushTargetMessenger); if (instance != null && transport.isPresent()) { unifiedPushBroker.rebroadcastEndpoint(messenger, instance, transport.get()); } @@ -814,7 +939,7 @@ public class XmppConnectionService extends Service { } return START_NOT_STICKY; } - final var extras = intent == null ? null : intent.getExtras(); + final var extras = intent == null ? null : intent.getExtras(); try { internalPingExecutor.execute(() -> manageAccountConnectionStates(action, extras)); } catch (final RejectedExecutionException e) { @@ -899,7 +1024,12 @@ public class XmppConnectionService extends Service { } } - private boolean processAccountState(final Account account, final boolean interactive, final boolean isUiAction, final boolean isAccountPushed, final HashSet pingCandidates) { + private boolean processAccountState( + final Account account, + final boolean interactive, + final boolean isUiAction, + final boolean isAccountPushed, + final HashSet pingCandidates) { if (!account.getStatus().isAttemptReconnect()) { return false; } @@ -915,9 +1045,17 @@ public class XmppConnectionService extends Service { synchronized (mLowPingTimeoutMode) { long lastReceived = account.getXmppConnection().getLastPacketReceived(); long lastSent = account.getXmppConnection().getLastPingSent(); - long pingInterval = isUiAction ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000; - long msToNextPing = (Math.max(lastReceived, lastSent) + pingInterval) - SystemClock.elapsedRealtime(); - int pingTimeout = mLowPingTimeoutMode.contains(account.getJid().asBareJid()) ? Config.LOW_PING_TIMEOUT * 1000 : Config.PING_TIMEOUT * 1000; + long pingInterval = + isUiAction + ? Config.PING_MIN_INTERVAL * 1000 + : Config.PING_MAX_INTERVAL * 1000; + long msToNextPing = + (Math.max(lastReceived, lastSent) + pingInterval) + - SystemClock.elapsedRealtime(); + int pingTimeout = + mLowPingTimeoutMode.contains(account.getJid().asBareJid()) + ? Config.LOW_PING_TIMEOUT * 1000 + : Config.PING_TIMEOUT * 1000; long pingTimeoutIn = (lastSent + pingTimeout) - SystemClock.elapsedRealtime(); if (lastSent > lastReceived) { if (pingTimeoutIn < 0) { @@ -931,15 +1069,22 @@ public class XmppConnectionService extends Service { pingCandidates.add(account); if (isAccountPushed) { if (mLowPingTimeoutMode.add(account.getJid().asBareJid())) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": entering low ping timeout mode"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": entering low ping timeout mode"); } return true; } else if (msToNextPing <= 0) { return true; } else { - this.scheduleWakeUpCall((int) (msToNextPing / 1000), account.getUuid().hashCode()); + this.scheduleWakeUpCall( + (int) (msToNextPing / 1000), account.getUuid().hashCode()); if (mLowPingTimeoutMode.remove(account.getJid().asBareJid())) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": leaving low ping timeout mode"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": leaving low ping timeout mode"); } } } @@ -947,22 +1092,37 @@ public class XmppConnectionService extends Service { } else if (account.getStatus() == Account.State.OFFLINE) { reconnectAccount(account, true, interactive); } else if (account.getStatus() == Account.State.CONNECTING) { - long secondsSinceLastConnect = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000; - long secondsSinceLastDisco = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastDiscoStarted()) / 1000; + long secondsSinceLastConnect = + (SystemClock.elapsedRealtime() + - account.getXmppConnection().getLastConnect()) + / 1000; + long secondsSinceLastDisco = + (SystemClock.elapsedRealtime() + - account.getXmppConnection().getLastDiscoStarted()) + / 1000; long discoTimeout = Config.CONNECT_DISCO_TIMEOUT - secondsSinceLastDisco; long timeout = Config.CONNECT_TIMEOUT - secondsSinceLastConnect; if (timeout < 0) { - Log.d(Config.LOGTAG, account.getJid() + ": time out during connect reconnecting (secondsSinceLast=" + secondsSinceLastConnect + ")"); + Log.d( + Config.LOGTAG, + account.getJid() + + ": time out during connect reconnecting (secondsSinceLast=" + + secondsSinceLastConnect + + ")"); account.getXmppConnection().resetAttemptCount(false); reconnectAccount(account, true, interactive); } else if (discoTimeout < 0) { account.getXmppConnection().sendDiscoTimeout(); - scheduleWakeUpCall((int) Math.min(timeout, discoTimeout), account.getUuid().hashCode()); + scheduleWakeUpCall( + (int) Math.min(timeout, discoTimeout), account.getUuid().hashCode()); } else { - scheduleWakeUpCall((int) Math.min(timeout, discoTimeout), account.getUuid().hashCode()); + scheduleWakeUpCall( + (int) Math.min(timeout, discoTimeout), account.getUuid().hashCode()); } } else { - final boolean aggressive = account.getStatus() == Account.State.SEE_OTHER_HOST || hasJingleRtpConnection(account); + final boolean aggressive = + account.getStatus() == Account.State.SEE_OTHER_HOST + || hasJingleRtpConnection(account); if (account.getXmppConnection().getTimeToNextAttempt(aggressive) <= 0) { reconnectAccount(account, true, interactive); } @@ -972,7 +1132,7 @@ public class XmppConnectionService extends Service { } private void toggleSoftDisabled(final boolean softDisabled) { - for(final Account account : this.accounts) { + for (final Account account : this.accounts) { if (account.isEnabled()) { if (account.setOption(Account.OPTION_SOFT_DISABLED, softDisabled)) { updateAccount(account); @@ -981,7 +1141,8 @@ public class XmppConnectionService extends Service { } } - public boolean processUnifiedPushMessage(final Account account, final Jid transport, final Element push) { + public boolean processUnifiedPushMessage( + final Account account, final Jid transport, final Element push) { return unifiedPushBroker.processPushMessage(account, transport, push); } @@ -989,8 +1150,12 @@ public class XmppConnectionService extends Service { mChannelDiscoveryService.initializeMuclumbusService(); } - public void discoverChannels(String query, ChannelDiscoveryService.Method method, ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { - mChannelDiscoveryService.discover(Strings.nullToEmpty(query).trim(), method, onChannelSearchResultsFound); + public void discoverChannels( + String query, + ChannelDiscoveryService.Method method, + ChannelDiscoveryService.OnChannelSearchResultsFound onChannelSearchResultsFound) { + mChannelDiscoveryService.discover( + Strings.nullToEmpty(query).trim(), method, onChannelSearchResultsFound); } public boolean isDataSaverDisabled() { @@ -1003,34 +1168,38 @@ public class XmppConnectionService extends Service { == ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; } - private void directReply(final Conversation conversation, final String body, final String lastMessageUuid, final boolean dismissAfterReply) { - final Message inReplyTo = lastMessageUuid == null ? null : conversation.findMessageWithUuid(lastMessageUuid); + private void directReply( + final Conversation conversation, + final String body, + final String lastMessageUuid, + final boolean dismissAfterReply) { + final Message inReplyTo = + lastMessageUuid == null ? null : conversation.findMessageWithUuid(lastMessageUuid); final Message message = new Message(conversation, body, conversation.getNextEncryption()); if (inReplyTo != null && inReplyTo.isPrivateMessage()) { Message.configurePrivateMessage(message, inReplyTo.getCounterpart()); } message.markUnread(); if (message.getEncryption() == Message.ENCRYPTION_PGP) { - getPgpEngine().encrypt(message, new UiCallback() { - @Override - public void success(Message message) { - if (dismissAfterReply) { - markRead((Conversation) message.getConversation(), true); - } else { - mNotificationService.pushFromDirectReply(message); - } - } - - @Override - public void error(int errorCode, Message object) { + getPgpEngine() + .encrypt( + message, + new UiCallback() { + @Override + public void success(Message message) { + if (dismissAfterReply) { + markRead((Conversation) message.getConversation(), true); + } else { + mNotificationService.pushFromDirectReply(message); + } + } - } + @Override + public void error(int errorCode, Message object) {} - @Override - public void userInputRequired(PendingIntent pi, Message object) { - - } - }); + @Override + public void userInputRequired(PendingIntent pi, Message object) {} + }); } else { sendMessage(message); if (dismissAfterReply) { @@ -1046,19 +1215,25 @@ public class XmppConnectionService extends Service { } private boolean manuallyChangePresence() { - return getBooleanPreference(AppSettings.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence); + return getBooleanPreference( + AppSettings.MANUALLY_CHANGE_PRESENCE, R.bool.manually_change_presence); } private boolean treatVibrateAsSilent() { - return getBooleanPreference(AppSettings.TREAT_VIBRATE_AS_SILENT, R.bool.treat_vibrate_as_silent); + return getBooleanPreference( + AppSettings.TREAT_VIBRATE_AS_SILENT, R.bool.treat_vibrate_as_silent); } private boolean awayWhenScreenLocked() { - return getBooleanPreference(AppSettings.AWAY_WHEN_SCREEN_IS_OFF, R.bool.away_when_screen_off); + return getBooleanPreference( + AppSettings.AWAY_WHEN_SCREEN_IS_OFF, R.bool.away_when_screen_off); } private String getCompressPicturesPreference() { - return getPreferences().getString("picture_compression", getResources().getString(R.string.picture_compression)); + return getPreferences() + .getString( + "picture_compression", + getResources().getString(R.string.picture_compression)); } private Presence.Status getTargetPresence() { @@ -1086,10 +1261,16 @@ public class XmppConnectionService extends Service { private boolean isPhoneSilenced() { final NotificationManager notificationManager = getSystemService(NotificationManager.class); - final int filter = notificationManager == null ? NotificationManager.INTERRUPTION_FILTER_UNKNOWN : notificationManager.getCurrentInterruptionFilter(); + final int filter = + notificationManager == null + ? NotificationManager.INTERRUPTION_FILTER_UNKNOWN + : notificationManager.getCurrentInterruptionFilter(); final boolean notificationDnd = filter >= NotificationManager.INTERRUPTION_FILTER_PRIORITY; final AudioManager audioManager = getSystemService(AudioManager.class); - final int ringerMode = audioManager == null ? AudioManager.RINGER_MODE_NORMAL : audioManager.getRingerMode(); + final int ringerMode = + audioManager == null + ? AudioManager.RINGER_MODE_NORMAL + : audioManager.getRingerMode(); try { if (treatVibrateAsSilent()) { return notificationDnd || ringerMode != AudioManager.RINGER_MODE_NORMAL; @@ -1097,7 +1278,9 @@ public class XmppConnectionService extends Service { return notificationDnd || ringerMode == AudioManager.RINGER_MODE_SILENT; } } catch (final Throwable throwable) { - Log.d(Config.LOGTAG, "platform bug in isPhoneSilenced (" + throwable.getMessage() + ")"); + Log.d( + Config.LOGTAG, + "platform bug in isPhoneSilenced (" + throwable.getMessage() + ")"); return notificationDnd; } } @@ -1121,7 +1304,9 @@ public class XmppConnectionService extends Service { private void dismissErrorNotifications() { for (final Account account : this.accounts) { if (account.hasErrorStatus()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": dismissing error notification"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": dismissing error notification"); if (account.setShowErrorNotification(false)) { mDatabaseWriterExecutor.execute(() -> databaseBackend.updateAccount(account)); } @@ -1135,41 +1320,50 @@ public class XmppConnectionService extends Service { public void expireOldMessages(final boolean resetHasMessagesLeftOnServer) { mLastExpiryRun.set(SystemClock.elapsedRealtime()); - mDatabaseWriterExecutor.execute(() -> { - long timestamp = getAutomaticMessageDeletionDate(); - if (timestamp > 0) { - databaseBackend.expireOldMessages(timestamp); - synchronized (XmppConnectionService.this.conversations) { - for (Conversation conversation : XmppConnectionService.this.conversations) { - conversation.expireOldMessages(timestamp); - if (resetHasMessagesLeftOnServer) { - conversation.messagesLoaded.set(true); - conversation.setHasMessagesLeftOnServer(true); + mDatabaseWriterExecutor.execute( + () -> { + long timestamp = getAutomaticMessageDeletionDate(); + if (timestamp > 0) { + databaseBackend.expireOldMessages(timestamp); + synchronized (XmppConnectionService.this.conversations) { + for (Conversation conversation : + XmppConnectionService.this.conversations) { + conversation.expireOldMessages(timestamp); + if (resetHasMessagesLeftOnServer) { + conversation.messagesLoaded.set(true); + conversation.setHasMessagesLeftOnServer(true); + } + } } + updateConversationUi(); } - } - updateConversationUi(); - } - }); + }); } public boolean hasInternetConnection() { - final ConnectivityManager cm = ContextCompat.getSystemService(this, ConnectivityManager.class); + final ConnectivityManager cm = + ContextCompat.getSystemService(this, ConnectivityManager.class); if (cm == null) { - return true; //if internet connection can not be checked it is probably best to just try + return true; // if internet connection can not be checked it is probably best to just + // try } try { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { final Network activeNetwork = cm.getActiveNetwork(); - final NetworkCapabilities capabilities = activeNetwork == null ? null : cm.getNetworkCapabilities(activeNetwork); - return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + final NetworkCapabilities capabilities = + activeNetwork == null ? null : cm.getNetworkCapabilities(activeNetwork); + return capabilities != null + && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); } else { final NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - return networkInfo != null && (networkInfo.isConnected() || networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET); + return networkInfo != null + && (networkInfo.isConnected() + || networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET); } } catch (final RuntimeException e) { Log.d(Config.LOGTAG, "unable to check for internet connection", e); - return true; //if internet connection can not be checked it is probably best to just try + return true; // if internet connection can not be checked it is probably best to just + // try } } @@ -1193,14 +1387,16 @@ public class XmppConnectionService extends Service { updateMemorizingTrustManager(); final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; - this.mBitmapCache = new LruCache(cacheSize) { - @Override - protected int sizeOf(final String key, final Bitmap bitmap) { - return bitmap.getByteCount() / 1024; - } - }; + this.mBitmapCache = + new LruCache(cacheSize) { + @Override + protected int sizeOf(final String key, final Bitmap bitmap) { + return bitmap.getByteCount() / 1024; + } + }; if (mLastActivity == 0) { - mLastActivity = getPreferences().getLong(SETTING_LAST_ACTIVITY_TS, System.currentTimeMillis()); + mLastActivity = + getPreferences().getLong(SETTING_LAST_ACTIVITY_TS, System.currentTimeMillis()); } Log.d(Config.LOGTAG, "initializing database..."); @@ -1232,22 +1428,30 @@ public class XmppConnectionService extends Service { FILE_OBSERVER_EXECUTOR.execute(this::checkForDeletedFiles); } if (Config.supportOpenPgp()) { - this.pgpServiceConnection = new OpenPgpServiceConnection(this, "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() { - @Override - public void onBound(final IOpenPgpService2 service) { - for (Account account : accounts) { - final PgpDecryptionService pgp = account.getPgpDecryptionService(); - if (pgp != null) { - pgp.continueDecryption(true); - } - } - } + this.pgpServiceConnection = + new OpenPgpServiceConnection( + this, + "org.sufficientlysecure.keychain", + new OpenPgpServiceConnection.OnBound() { + @Override + public void onBound(final IOpenPgpService2 service) { + for (Account account : accounts) { + final PgpDecryptionService pgp = + account.getPgpDecryptionService(); + if (pgp != null) { + pgp.continueDecryption(true); + } + } + } - @Override - public void onError(final Exception exception) { - Log.e(Config.LOGTAG,"could not bind to OpenKeyChain", exception); - } - }); + @Override + public void onError(final Exception exception) { + Log.e( + Config.LOGTAG, + "could not bind to OpenKeyChain", + exception); + } + }); this.pgpServiceConnection.bindToService(); } @@ -1281,32 +1485,39 @@ public class XmppConnectionService extends Service { ContextCompat.RECEIVER_EXPORTED); mForceDuringOnCreate.set(false); toggleForegroundService(); - internalPingExecutor.scheduleAtFixedRate(this::manageAccountConnectionStatesInternal,10,10,TimeUnit.SECONDS); + internalPingExecutor.scheduleAtFixedRate( + this::manageAccountConnectionStatesInternal, 10, 10, TimeUnit.SECONDS); final SharedPreferences sharedPreferences = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this); - sharedPreferences.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { - Log.d(Config.LOGTAG,"preference '"+key+"' has changed"); - if (AppSettings.KEEP_FOREGROUND_SERVICE.equals(key)) { - toggleForegroundService(); - } - } - }); + sharedPreferences.registerOnSharedPreferenceChangeListener( + new SharedPreferences.OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, @Nullable String key) { + Log.d(Config.LOGTAG, "preference '" + key + "' has changed"); + if (AppSettings.KEEP_FOREGROUND_SERVICE.equals(key)) { + toggleForegroundService(); + } + } + }); } - private void checkForDeletedFiles() { if (destroyed) { - Log.d(Config.LOGTAG, "Do not check for deleted files because service has been destroyed"); + Log.d( + Config.LOGTAG, + "Do not check for deleted files because service has been destroyed"); return; } final long start = SystemClock.elapsedRealtime(); - final List relativeFilePaths = databaseBackend.getFilePathInfo(); + final List relativeFilePaths = + databaseBackend.getFilePathInfo(); final List changed = new ArrayList<>(); for (final DatabaseBackend.FilePathInfo filePath : relativeFilePaths) { if (destroyed) { - Log.d(Config.LOGTAG, "Stop checking for deleted files because service has been destroyed"); + Log.d( + Config.LOGTAG, + "Stop checking for deleted files because service has been destroyed"); return; } final File file = fileBackend.getFileForPath(filePath.path); @@ -1315,7 +1526,15 @@ public class XmppConnectionService extends Service { } } final long duration = SystemClock.elapsedRealtime() - start; - Log.d(Config.LOGTAG, "found " + changed.size() + " changed files on start up. total=" + relativeFilePaths.size() + ". (" + duration + "ms)"); + Log.d( + Config.LOGTAG, + "found " + + changed.size() + + " changed files on start up. total=" + + relativeFilePaths.size() + + ". (" + + duration + + "ms)"); if (changed.size() > 0) { databaseBackend.markFilesAsChanged(changed); markChangedFiles(changed); @@ -1323,15 +1542,19 @@ public class XmppConnectionService extends Service { } public void startContactObserver() { - getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, new ContentObserver(null) { - @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - if (restoredFromDatabaseLatch.getCount() == 0) { - loadPhoneContacts(); - } - } - }); + getContentResolver() + .registerContentObserver( + ContactsContract.Contacts.CONTENT_URI, + true, + new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + if (restoredFromDatabaseLatch.getCount() == 0) { + loadPhoneContacts(); + } + } + }); } @Override @@ -1350,7 +1573,7 @@ public class XmppConnectionService extends Service { unregisterReceiver(this.mInternalRestrictedEventReceiver); unregisterReceiver(this.mInternalScreenEventReceiver); } catch (final IllegalArgumentException e) { - //ignored + // ignored } destroyed = false; fileObserver.stopWatching(); @@ -1375,7 +1598,7 @@ public class XmppConnectionService extends Service { try { unregisterReceiver(this.mInternalScreenEventReceiver); } catch (IllegalArgumentException e) { - //ignored + // ignored } } } @@ -1384,7 +1607,8 @@ public class XmppConnectionService extends Service { toggleForegroundService(false); } - public void setOngoingCall(AbstractJingleConnection.Id id, Set media, final boolean reconnecting) { + public void setOngoingCall( + AbstractJingleConnection.Id id, Set media, final boolean reconnecting) { ongoingCall.set(new OngoingCall(id, media, reconnecting)); toggleForegroundService(false); } @@ -1473,13 +1697,18 @@ public class XmppConnectionService extends Service { } public boolean foregroundNotificationNeedsUpdatingWhenErrorStateChanges() { - return !mOngoingVideoTranscoding.get() && ongoingCall.get() == null && Compatibility.keepForegroundService(this) && hasEnabledAccounts(); + return !mOngoingVideoTranscoding.get() + && ongoingCall.get() == null + && Compatibility.keepForegroundService(this) + && hasEnabledAccounts(); } @Override public void onTaskRemoved(final Intent rootIntent) { super.onTaskRemoved(rootIntent); - if ((Compatibility.keepForegroundService(this) && hasEnabledAccounts()) || mOngoingVideoTranscoding.get() || ongoingCall.get() != null) { + if ((Compatibility.keepForegroundService(this) && hasEnabledAccounts()) + || mOngoingVideoTranscoding.get() + || ongoingCall.get() != null) { Log.d(Config.LOGTAG, "ignoring onTaskRemoved because foreground service is activated"); } else { this.logoutAndSave(false); @@ -1508,17 +1737,27 @@ public class XmppConnectionService extends Service { if (alarmManager == null) { return; } - final long triggerAtMillis = SystemClock.elapsedRealtime() + (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL * 1000); + final long triggerAtMillis = + SystemClock.elapsedRealtime() + + (Config.POST_CONNECTIVITY_CHANGE_PING_INTERVAL * 1000); final Intent intent = new Intent(this, SystemEventReceiver.class); intent.setAction(ACTION_POST_CONNECTIVITY_CHANGE); try { - final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, s() - ? PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT - : PendingIntent.FLAG_UPDATE_CURRENT); + final PendingIntent pendingIntent = + PendingIntent.getBroadcast( + this, + 1, + intent, + s() + ? PendingIntent.FLAG_IMMUTABLE + | PendingIntent.FLAG_UPDATE_CURRENT + : PendingIntent.FLAG_UPDATE_CURRENT); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent); + alarmManager.setAndAllowWhileIdle( + AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent); } else { - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent); + alarmManager.set( + AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent); } } catch (RuntimeException e) { Log.e(Config.LOGTAG, "unable to schedule alarm for post connectivity change", e); @@ -1526,7 +1765,8 @@ public class XmppConnectionService extends Service { } public void scheduleWakeUpCall(final int seconds, final int requestCode) { - final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000L; + final long timeToWake = + SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000L; final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); if (alarmManager == null) { return; @@ -1553,10 +1793,17 @@ public class XmppConnectionService extends Service { final Intent intent = new Intent(this, SystemEventReceiver.class); intent.setAction(ACTION_IDLE_PING); try { - final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, s() - ? PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT - : PendingIntent.FLAG_UPDATE_CURRENT); - alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, pendingIntent); + final PendingIntent pendingIntent = + PendingIntent.getBroadcast( + this, + 0, + intent, + s() + ? PendingIntent.FLAG_IMMUTABLE + | PendingIntent.FLAG_UPDATE_CURRENT + : PendingIntent.FLAG_UPDATE_CURRENT); + alarmManager.setAndAllowWhileIdle( + AlarmManager.ELAPSED_REALTIME_WAKEUP, timeToWake, pendingIntent); } catch (RuntimeException e) { Log.d(Config.LOGTAG, "unable to schedule alarm for idle ping", e); } @@ -1607,11 +1854,16 @@ public class XmppConnectionService extends Service { final Conversation conversation = (Conversation) message.getConversation(); account.deactivateGracePeriod(); - - if (QuickConversationsService.isQuicksy() && conversation.getMode() == Conversation.MODE_SINGLE) { + if (QuickConversationsService.isQuicksy() + && conversation.getMode() == Conversation.MODE_SINGLE) { final Contact contact = conversation.getContact(); if (!contact.showInRoster() && contact.getOption(Contact.Options.SYNCED_VIA_OTHER)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": adding " + contact.getJid() + " on sending message"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": adding " + + contact.getJid() + + " on sending message"); createContact(contact, true); } } @@ -1621,20 +1873,23 @@ public class XmppConnectionService extends Service { boolean saveInDb = addToConversation; message.setStatus(Message.STATUS_WAITING); - if (message.getEncryption() != Message.ENCRYPTION_NONE && conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous()) { - if (conversation.setAttribute(Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, true)) { + if (message.getEncryption() != Message.ENCRYPTION_NONE + && conversation.getMode() == Conversation.MODE_MULTI + && conversation.isPrivateAndNonAnonymous()) { + if (conversation.setAttribute( + Conversation.ATTRIBUTE_FORMERLY_PRIVATE_NON_ANONYMOUS, true)) { databaseBackend.updateConversation(conversation); } } final boolean inProgressJoin = isJoinInProgress(conversation); - if (account.isOnlineAndConnected() && !inProgressJoin) { switch (message.getEncryption()) { case Message.ENCRYPTION_NONE: if (message.needsUploading()) { - if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize()) + if (account.httpUploadAvailable( + fileBackend.getFile(message, false).getSize()) || conversation.getMode() == Conversation.MODE_MULTI || message.fixCounterpart()) { this.sendFileMessage(message, delay); @@ -1648,7 +1903,8 @@ public class XmppConnectionService extends Service { case Message.ENCRYPTION_PGP: case Message.ENCRYPTION_DECRYPTED: if (message.needsUploading()) { - if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize()) + if (account.httpUploadAvailable( + fileBackend.getFile(message, false).getSize()) || conversation.getMode() == Conversation.MODE_MULTI || message.fixCounterpart()) { this.sendFileMessage(message, delay); @@ -1662,7 +1918,8 @@ public class XmppConnectionService extends Service { case Message.ENCRYPTION_AXOLOTL: message.setFingerprint(account.getAxolotlService().getOwnFingerprint()); if (message.needsUploading()) { - if (account.httpUploadAvailable(fileBackend.getFile(message, false).getSize()) + if (account.httpUploadAvailable( + fileBackend.getFile(message, false).getSize()) || conversation.getMode() == Conversation.MODE_MULTI || message.fixCounterpart()) { this.sendFileMessage(message, delay); @@ -1670,7 +1927,8 @@ public class XmppConnectionService extends Service { break; } } else { - XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message); + XmppAxolotlMessage axolotlMessage = + account.getAxolotlService().fetchAxolotlMessageFromCache(message); if (axolotlMessage == null) { account.getAxolotlService().preparePayloadMessage(message, delay); } else { @@ -1678,11 +1936,11 @@ public class XmppConnectionService extends Service { } } break; - } if (packet != null) { if (account.getXmppConnection().getFeatures().sm() - || (conversation.getMode() == Conversation.MODE_MULTI && message.getCounterpart().isBareJid())) { + || (conversation.getMode() == Conversation.MODE_MULTI + && message.getCounterpart().isBareJid())) { message.setStatus(Message.STATUS_UNSEND); } else { message.setStatus(Message.STATUS_SEND); @@ -1694,7 +1952,7 @@ public class XmppConnectionService extends Service { if (!message.needsUploading()) { String pgpBody = message.getEncryptedBody(); String decryptedBody = message.getBody(); - message.setBody(pgpBody); //TODO might throw NPE + message.setBody(pgpBody); // TODO might throw NPE message.setEncryption(Message.ENCRYPTION_PGP); if (message.edited()) { message.setBody(decryptedBody); @@ -1718,8 +1976,8 @@ public class XmppConnectionService extends Service { } } - - boolean mucMessage = conversation.getMode() == Conversation.MODE_MULTI && !message.isPrivateMessage(); + boolean mucMessage = + conversation.getMode() == Conversation.MODE_MULTI && !message.isPrivateMessage(); if (mucMessage) { message.setCounterpart(conversation.getMucOptions().getSelf().getFullJid()); } @@ -1766,7 +2024,13 @@ public class XmppConnectionService extends Service { final boolean pending = account.pendingConferenceJoins.contains(conversation); final boolean inProgressJoin = inProgress || pending; if (inProgressJoin) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": holding back message to group. inProgress=" + inProgress + ", pending=" + pending); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": holding back message to group. inProgress=" + + inProgress + + ", pending=" + + pending); } return inProgressJoin; } else { @@ -1783,11 +2047,16 @@ public class XmppConnectionService extends Service { sendMessage(message, true, delay); } - public void requestEasyOnboardingInvite(final Account account, final EasyOnboardingInvite.OnInviteRequested callback) { + public void requestEasyOnboardingInvite( + final Account account, final EasyOnboardingInvite.OnInviteRequested callback) { final XmppConnection connection = account.getXmppConnection(); - final Jid jid = connection == null ? null : connection.getJidForCommand(Namespace.EASY_ONBOARDING_INVITE); + final Jid jid = + connection == null + ? null + : connection.getJidForCommand(Namespace.EASY_ONBOARDING_INVITE); if (jid == null) { - callback.inviteRequestFailed(getString(R.string.server_does_not_support_easy_onboarding_invites)); + callback.inviteRequestFailed( + getString(R.string.server_does_not_support_easy_onboarding_invites)); return; } final Iq request = new Iq(Iq.Type.SET); @@ -1795,57 +2064,72 @@ public class XmppConnectionService extends Service { final Element command = request.addChild("command", Namespace.COMMANDS); command.setAttribute("node", Namespace.EASY_ONBOARDING_INVITE); command.setAttribute("action", "execute"); - sendIqPacket(account, request, (response) -> { - if (response.getType() == Iq.Type.RESULT) { - final Element resultCommand = response.findChild("command", Namespace.COMMANDS); - final Element x = resultCommand == null ? null : resultCommand.findChild("x", Namespace.DATA); - if (x != null) { - final Data data = Data.parse(x); - final String uri = data.getValue("uri"); - final String landingUrl = data.getValue("landing-url"); - if (uri != null) { - final EasyOnboardingInvite invite = new EasyOnboardingInvite(jid.getDomain().toEscapedString(), uri, landingUrl); - callback.inviteRequested(invite); - return; + sendIqPacket( + account, + request, + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + final Element resultCommand = + response.findChild("command", Namespace.COMMANDS); + final Element x = + resultCommand == null + ? null + : resultCommand.findChild("x", Namespace.DATA); + if (x != null) { + final Data data = Data.parse(x); + final String uri = data.getValue("uri"); + final String landingUrl = data.getValue("landing-url"); + if (uri != null) { + final EasyOnboardingInvite invite = + new EasyOnboardingInvite( + jid.getDomain().toEscapedString(), uri, landingUrl); + callback.inviteRequested(invite); + return; + } + } + callback.inviteRequestFailed(getString(R.string.unable_to_parse_invite)); + Log.d(Config.LOGTAG, response.toString()); + } else if (response.getType() == Iq.Type.ERROR) { + callback.inviteRequestFailed(IqParser.errorMessage(response)); + } else { + callback.inviteRequestFailed(getString(R.string.remote_server_timeout)); } - } - callback.inviteRequestFailed(getString(R.string.unable_to_parse_invite)); - Log.d(Config.LOGTAG, response.toString()); - } else if (response.getType() == Iq.Type.ERROR) { - callback.inviteRequestFailed(IqParser.errorMessage(response)); - } else { - callback.inviteRequestFailed(getString(R.string.remote_server_timeout)); - } - }); - + }); } public void fetchBookmarks(final Account account) { final Iq iqPacket = new Iq(Iq.Type.GET); final Element query = iqPacket.query("jabber:iq:private"); query.addChild("storage", Namespace.BOOKMARKS); - final Consumer callback = (response) -> { - if (response.getType() == Iq.Type.RESULT) { - final Element query1 = response.query(); - final Element storage = query1.findChild("storage", "storage:bookmarks"); - Map bookmarks = Bookmark.parseFromStorage(storage, account); - processBookmarksInitial(account, bookmarks, false); - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not fetch bookmarks"); - } - }; + final Consumer callback = + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + final Element query1 = response.query(); + final Element storage = query1.findChild("storage", "storage:bookmarks"); + Map bookmarks = Bookmark.parseFromStorage(storage, account); + processBookmarksInitial(account, bookmarks, false); + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": could not fetch bookmarks"); + } + }; sendIqPacket(account, iqPacket, callback); } public void fetchBookmarks2(final Account account) { final Iq retrieve = mIqGenerator.retrieveBookmarks(); - sendIqPacket(account, retrieve, (response) -> { - if (response.getType() == Iq.Type.RESULT) { - final Element pubsub = response.findChild("pubsub", Namespace.PUBSUB); - final Map bookmarks = Bookmark.parseFromPubSub(pubsub, account); - processBookmarksInitial(account, bookmarks, true); - } - }); + sendIqPacket( + account, + retrieve, + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + final Element pubsub = response.findChild("pubsub", Namespace.PUBSUB); + final Map bookmarks = + Bookmark.parseFromPubSub(pubsub, account); + processBookmarksInitial(account, bookmarks, true); + } + }); } public void fetchMessageDisplayedSynchronization(final Account account) { @@ -1921,7 +2205,8 @@ public class XmppConnectionService extends Service { return true; } - public void processBookmarksInitial(final Account account, final Map bookmarks, final boolean pep) { + public void processBookmarksInitial( + final Account account, final Map bookmarks, final boolean pep) { final Set previousBookmarks = account.getBookmarkedJids(); for (final Bookmark bookmark : bookmarks.values()) { previousBookmarks.remove(bookmark.getJid().asBareJid()); @@ -1965,7 +2250,12 @@ public class XmppConnectionService extends Service { } bookmark.setConversation(conversation); if (pep && !bookmark.autojoin()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving conference (" + conversation.getJid() + ") after receiving pep"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": archiving conference (" + + conversation.getJid() + + ") after receiving pep"); archiveConversation(conversation, false); } else { final MucOptions mucOptions = conversation.getMucOptions(); @@ -1973,7 +2263,13 @@ public class XmppConnectionService extends Service { final String current = mucOptions.getActualNick(); final String proposed = mucOptions.getProposedNickPure(); if (current != null && !current.equals(proposed)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": proposed nick changed after bookmark push " + current + "->" + proposed); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": proposed nick changed after bookmark push " + + current + + "->" + + proposed); joinMuc(conversation); } } else { @@ -1981,7 +2277,8 @@ public class XmppConnectionService extends Service { } } } else if (bookmark.autojoin()) { - conversation = findOrCreateConversation(account, bookmark.getFullJid(), true, true, false); + conversation = + findOrCreateConversation(account, bookmark.getFullJid(), true, true, false); bookmark.setConversation(conversation); } } @@ -1994,11 +2291,20 @@ public class XmppConnectionService extends Service { account.putBookmark(bookmark); final XmppConnection connection = account.getXmppConnection(); if (connection == null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": no connection. ignoring bookmark creation"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": no connection. ignoring bookmark creation"); } else if (connection.getFeatures().bookmarks2()) { - Log.d(Config.LOGTAG,account.getJid().asBareJid() + ": pushing bookmark via Bookmarks 2"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": pushing bookmark via Bookmarks 2"); final Element item = mIqGenerator.publishBookmarkItem(bookmark); - pushNodeAndEnforcePublishOptions(account, Namespace.BOOKMARKS2, item, bookmark.getJid().asBareJid().toEscapedString(), PublishOptions.persistentWhitelistAccessMaxItems()); + pushNodeAndEnforcePublishOptions( + account, + Namespace.BOOKMARKS2, + item, + bookmark.getJid().asBareJid().toEscapedString(), + PublishOptions.persistentWhitelistAccessMaxItems()); } else if (connection.getFeatures().bookmarksConversion()) { pushBookmarksPep(account); } else { @@ -2010,13 +2316,24 @@ public class XmppConnectionService extends Service { account.removeBookmark(bookmark); final XmppConnection connection = account.getXmppConnection(); if (connection.getFeatures().bookmarks2()) { - final Iq request = mIqGenerator.deleteItem(Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString()); - Log.d(Config.LOGTAG,account.getJid().asBareJid() + ": removing bookmark via Bookmarks 2"); - sendIqPacket(account, request, (response) -> { - if (response.getType() == Iq.Type.ERROR) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to delete bookmark " + response.getErrorCondition()); - } - }); + final Iq request = + mIqGenerator.deleteItem( + Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString()); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": removing bookmark via Bookmarks 2"); + sendIqPacket( + account, + request, + (response) -> { + if (response.getType() == Iq.Type.ERROR) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": unable to delete bookmark " + + response.getErrorCondition()); + } + }); } else if (connection.getFeatures().bookmarksConversion()) { pushBookmarksPep(account); } else { @@ -2041,37 +2358,72 @@ public class XmppConnectionService extends Service { for (final Bookmark bookmark : account.getBookmarks()) { storage.addChild(bookmark); } - pushNodeAndEnforcePublishOptions(account, Namespace.BOOKMARKS, storage, "current", PublishOptions.persistentWhitelistAccess()); - - } - - private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final String id, final Bundle options) { + pushNodeAndEnforcePublishOptions( + account, + Namespace.BOOKMARKS, + storage, + "current", + PublishOptions.persistentWhitelistAccess()); + } + + private void pushNodeAndEnforcePublishOptions( + final Account account, + final String node, + final Element element, + final String id, + final Bundle options) { pushNodeAndEnforcePublishOptions(account, node, element, id, options, true); - } - private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final String id, final Bundle options, final boolean retry) { + private void pushNodeAndEnforcePublishOptions( + final Account account, + final String node, + final Element element, + final String id, + final Bundle options, + final boolean retry) { final Iq packet = mIqGenerator.publishElement(node, element, id, options); - sendIqPacket(account, packet, (response) -> { - if (response.getType() == Iq.Type.RESULT) { - return; - } - if (retry && PublishOptions.preconditionNotMet(response)) { - pushNodeConfiguration(account, node, options, new OnConfigurationPushed() { - @Override - public void onPushSucceeded() { - pushNodeAndEnforcePublishOptions(account, node, element, id, options, false); + sendIqPacket( + account, + packet, + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + return; } + if (retry && PublishOptions.preconditionNotMet(response)) { + pushNodeConfiguration( + account, + node, + options, + new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + pushNodeAndEnforcePublishOptions( + account, node, element, id, options, false); + } - @Override - public void onPushFailed() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to push node configuration (" + node + ")"); + @Override + public void onPushFailed() { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": unable to push node configuration (" + + node + + ")"); + } + }); + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": error publishing " + + node + + " (retry=" + + retry + + ") " + + response); } }); - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error publishing "+node+" (retry=" + retry + ") " + response); - } - }); } private void restoreFromDatabase() { @@ -2082,97 +2434,127 @@ public class XmppConnectionService extends Service { } Log.d(Config.LOGTAG, "restoring conversations..."); final long startTimeConversationsRestore = SystemClock.elapsedRealtime(); - this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE)); - for (Iterator iterator = conversations.listIterator(); iterator.hasNext(); ) { + this.conversations.addAll( + databaseBackend.getConversations(Conversation.STATUS_AVAILABLE)); + for (Iterator iterator = conversations.listIterator(); + iterator.hasNext(); ) { Conversation conversation = iterator.next(); Account account = accountLookupTable.get(conversation.getAccountUuid()); if (account != null) { conversation.setAccount(account); } else { - Log.e(Config.LOGTAG, "unable to restore Conversations with " + conversation.getJid()); + Log.e( + Config.LOGTAG, + "unable to restore Conversations with " + conversation.getJid()); iterator.remove(); } } - long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore; - Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms"); - Runnable runnable = () -> { - if (DatabaseBackend.requiresMessageIndexRebuild()) { - DatabaseBackend.getInstance(this).rebuildMessagesIndex(); - } - final long deletionDate = getAutomaticMessageDeletionDate(); - mLastExpiryRun.set(SystemClock.elapsedRealtime()); - if (deletionDate > 0) { - Log.d(Config.LOGTAG, "deleting messages that are older than " + AbstractGenerator.getTimestamp(deletionDate)); - databaseBackend.expireOldMessages(deletionDate); - } - Log.d(Config.LOGTAG, "restoring roster..."); - for (final Account account : accounts) { - databaseBackend.readRoster(account.getRoster()); - account.initAccountServices(XmppConnectionService.this); //roster needs to be loaded at this stage - } - getBitmapCache().evictAll(); - loadPhoneContacts(); - Log.d(Config.LOGTAG, "restoring messages..."); - final long startMessageRestore = SystemClock.elapsedRealtime(); - final Conversation quickLoad = QuickLoader.get(this.conversations); - if (quickLoad != null) { - restoreMessages(quickLoad); - updateConversationUi(); - final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore; - Log.d(Config.LOGTAG, "quickly restored " + quickLoad.getName() + " after " + diffMessageRestore + "ms"); - } - for (Conversation conversation : this.conversations) { - if (quickLoad != conversation) { - restoreMessages(conversation); - } - } - mNotificationService.finishBacklog(); - restoredFromDatabaseLatch.countDown(); - final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore; - Log.d(Config.LOGTAG, "finished restoring messages in " + diffMessageRestore + "ms"); - updateConversationUi(); - }; - mDatabaseReaderExecutor.execute(runnable); //will contain one write command (expiry) but that's fine + long diffConversationsRestore = + SystemClock.elapsedRealtime() - startTimeConversationsRestore; + Log.d( + Config.LOGTAG, + "finished restoring conversations in " + diffConversationsRestore + "ms"); + Runnable runnable = + () -> { + if (DatabaseBackend.requiresMessageIndexRebuild()) { + DatabaseBackend.getInstance(this).rebuildMessagesIndex(); + } + final long deletionDate = getAutomaticMessageDeletionDate(); + mLastExpiryRun.set(SystemClock.elapsedRealtime()); + if (deletionDate > 0) { + Log.d( + Config.LOGTAG, + "deleting messages that are older than " + + AbstractGenerator.getTimestamp(deletionDate)); + databaseBackend.expireOldMessages(deletionDate); + } + Log.d(Config.LOGTAG, "restoring roster..."); + for (final Account account : accounts) { + databaseBackend.readRoster(account.getRoster()); + account.initAccountServices( + XmppConnectionService + .this); // roster needs to be loaded at this stage + } + getBitmapCache().evictAll(); + loadPhoneContacts(); + Log.d(Config.LOGTAG, "restoring messages..."); + final long startMessageRestore = SystemClock.elapsedRealtime(); + final Conversation quickLoad = QuickLoader.get(this.conversations); + if (quickLoad != null) { + restoreMessages(quickLoad); + updateConversationUi(); + final long diffMessageRestore = + SystemClock.elapsedRealtime() - startMessageRestore; + Log.d( + Config.LOGTAG, + "quickly restored " + + quickLoad.getName() + + " after " + + diffMessageRestore + + "ms"); + } + for (Conversation conversation : this.conversations) { + if (quickLoad != conversation) { + restoreMessages(conversation); + } + } + mNotificationService.finishBacklog(); + restoredFromDatabaseLatch.countDown(); + final long diffMessageRestore = + SystemClock.elapsedRealtime() - startMessageRestore; + Log.d( + Config.LOGTAG, + "finished restoring messages in " + diffMessageRestore + "ms"); + updateConversationUi(); + }; + mDatabaseReaderExecutor.execute( + runnable); // will contain one write command (expiry) but that's fine } } private void restoreMessages(Conversation conversation) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); - conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); + conversation.findUnsentTextMessages( + message -> markMessage(message, Message.STATUS_WAITING)); conversation.findUnreadMessagesAndCalls(mNotificationService::pushFromBacklog); } public void loadPhoneContacts() { - mContactMergerExecutor.execute(() -> { - final Map contacts = JabberIdContact.load(this); - Log.d(Config.LOGTAG, "start merging phone contacts with roster"); - for (final Account account : accounts) { - final List withSystemAccounts = account.getRoster().getWithSystemAccounts(JabberIdContact.class); - for (final JabberIdContact jidContact : contacts.values()) { - final Contact contact = account.getRoster().getContact(jidContact.getJid()); - boolean needsCacheClean = contact.setPhoneContact(jidContact); - if (needsCacheClean) { - getAvatarService().clear(contact); - } - withSystemAccounts.remove(contact); - } - for (final Contact contact : withSystemAccounts) { - boolean needsCacheClean = contact.unsetPhoneContact(JabberIdContact.class); - if (needsCacheClean) { - getAvatarService().clear(contact); + mContactMergerExecutor.execute( + () -> { + final Map contacts = JabberIdContact.load(this); + Log.d(Config.LOGTAG, "start merging phone contacts with roster"); + for (final Account account : accounts) { + final List withSystemAccounts = + account.getRoster().getWithSystemAccounts(JabberIdContact.class); + for (final JabberIdContact jidContact : contacts.values()) { + final Contact contact = + account.getRoster().getContact(jidContact.getJid()); + boolean needsCacheClean = contact.setPhoneContact(jidContact); + if (needsCacheClean) { + getAvatarService().clear(contact); + } + withSystemAccounts.remove(contact); + } + for (final Contact contact : withSystemAccounts) { + boolean needsCacheClean = + contact.unsetPhoneContact(JabberIdContact.class); + if (needsCacheClean) { + getAvatarService().clear(contact); + } + } } - } - } - Log.d(Config.LOGTAG, "finished merging phone contacts"); - mShortcutService.refresh(mInitialAddressbookSyncCompleted.compareAndSet(false, true)); - updateRosterUi(); - mQuickConversationsService.considerSync(); - }); + Log.d(Config.LOGTAG, "finished merging phone contacts"); + mShortcutService.refresh( + mInitialAddressbookSyncCompleted.compareAndSet(false, true)); + updateRosterUi(); + mQuickConversationsService.considerSync(); + }); } - public void syncRoster(final Account account) { - mRosterSyncTaskManager.execute(account, () -> databaseBackend.writeRoster(account.getRoster())); + mRosterSyncTaskManager.execute( + account, () -> databaseBackend.writeRoster(account.getRoster())); } public List getConversations() { @@ -2188,7 +2570,14 @@ public class XmppConnectionService extends Service { } final boolean isInternalFile = fileBackend.isInternalFile(file); final List uuids = databaseBackend.markFileAsDeleted(file, isInternalFile); - Log.d(Config.LOGTAG, "deleted file " + file.getAbsolutePath() + " internal=" + isInternalFile + ", database hits=" + uuids.size()); + Log.d( + Config.LOGTAG, + "deleted file " + + file.getAbsolutePath() + + " internal=" + + isInternalFile + + ", database hits=" + + uuids.size()); markUuidsAsDeletedFiles(uuids); } @@ -2219,11 +2608,13 @@ public class XmppConnectionService extends Service { populateWithOrderedConversations(list, true, true); } - public void populateWithOrderedConversations(final List list, final boolean includeNoFileUpload) { + public void populateWithOrderedConversations( + final List list, final boolean includeNoFileUpload) { populateWithOrderedConversations(list, includeNoFileUpload, true); } - public void populateWithOrderedConversations(final List list, final boolean includeNoFileUpload, final boolean sort) { + public void populateWithOrderedConversations( + final List list, final boolean includeNoFileUpload, final boolean sort) { final List orderedUuids; if (sort) { orderedUuids = null; @@ -2239,63 +2630,85 @@ public class XmppConnectionService extends Service { } else { for (Conversation conversation : getConversations()) { if (conversation.getMode() == Conversation.MODE_SINGLE - || (conversation.getAccount().httpUploadAvailable() && conversation.getMucOptions().participating())) { + || (conversation.getAccount().httpUploadAvailable() + && conversation.getMucOptions().participating())) { list.add(conversation); } } } try { if (orderedUuids != null) { - Collections.sort(list, (a, b) -> { - final int indexA = orderedUuids.indexOf(a.getUuid()); - final int indexB = orderedUuids.indexOf(b.getUuid()); - if (indexA == -1 || indexB == -1 || indexA == indexB) { - return a.compareTo(b); - } - return indexA - indexB; - }); + Collections.sort( + list, + (a, b) -> { + final int indexA = orderedUuids.indexOf(a.getUuid()); + final int indexB = orderedUuids.indexOf(b.getUuid()); + if (indexA == -1 || indexB == -1 || indexA == indexB) { + return a.compareTo(b); + } + return indexA - indexB; + }); } else { Collections.sort(list); } } catch (IllegalArgumentException e) { - //ignore + // ignore } } - public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) { - if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) { + public void loadMoreMessages( + final Conversation conversation, + final long timestamp, + final OnMoreMessagesLoaded callback) { + if (XmppConnectionService.this + .getMessageArchiveService() + .queryInProgress(conversation, callback)) { return; } else if (timestamp == 0) { return; } - Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp)); - final Runnable runnable = () -> { - final Account account = conversation.getAccount(); - List messages = databaseBackend.getMessages(conversation, 50, timestamp); - if (messages.size() > 0) { - conversation.addAll(0, messages); - callback.onMoreMessagesLoaded(messages.size(), conversation); - } else if (conversation.hasMessagesLeftOnServer() - && account.isOnlineAndConnected() - && conversation.getLastClearHistory().getTimestamp() == 0) { - final boolean mamAvailable; - if (conversation.getMode() == Conversation.MODE_SINGLE) { - mamAvailable = account.getXmppConnection().getFeatures().mam() && !conversation.getContact().isBlocked(); - } else { - mamAvailable = conversation.getMucOptions().mamSupport(); - } - if (mamAvailable) { - MessageArchiveService.Query query = getMessageArchiveService().query(conversation, new MamReference(0), timestamp, false); - if (query != null) { - query.setCallback(callback); - callback.informUser(R.string.fetching_history_from_server); - } else { - callback.informUser(R.string.not_fetching_history_retention_period); + Log.d( + Config.LOGTAG, + "load more messages for " + + conversation.getName() + + " prior to " + + MessageGenerator.getTimestamp(timestamp)); + final Runnable runnable = + () -> { + final Account account = conversation.getAccount(); + List messages = + databaseBackend.getMessages(conversation, 50, timestamp); + if (messages.size() > 0) { + conversation.addAll(0, messages); + callback.onMoreMessagesLoaded(messages.size(), conversation); + } else if (conversation.hasMessagesLeftOnServer() + && account.isOnlineAndConnected() + && conversation.getLastClearHistory().getTimestamp() == 0) { + final boolean mamAvailable; + if (conversation.getMode() == Conversation.MODE_SINGLE) { + mamAvailable = + account.getXmppConnection().getFeatures().mam() + && !conversation.getContact().isBlocked(); + } else { + mamAvailable = conversation.getMucOptions().mamSupport(); + } + if (mamAvailable) { + MessageArchiveService.Query query = + getMessageArchiveService() + .query( + conversation, + new MamReference(0), + timestamp, + false); + if (query != null) { + query.setCallback(callback); + callback.informUser(R.string.fetching_history_from_server); + } else { + callback.informUser(R.string.not_fetching_history_retention_period); + } + } } - - } - } - }; + }; mDatabaseReaderExecutor.execute(runnable); } @@ -2303,9 +2716,9 @@ public class XmppConnectionService extends Service { return this.accounts; } - /** - * This will find all conferences with the contact as member and also the conference that is the contact (that 'fake' contact is used to store the avatar) + * This will find all conferences with the contact as member and also the conference that is the + * contact (that 'fake' contact is used to store the avatar) */ public List findAllConferencesWith(Contact contact) { final ArrayList results = new ArrayList<>(); @@ -2314,7 +2727,8 @@ public class XmppConnectionService extends Service { continue; } final MucOptions mucOptions = c.getMucOptions(); - if (c.getJid().asBareJid().equals(contact.getJid().asBareJid()) || (mucOptions != null && mucOptions.isContactInRoom(contact))) { + if (c.getJid().asBareJid().equals(contact.getJid().asBareJid()) + || (mucOptions != null && mucOptions.isContactInRoom(contact))) { results.add(c); } } @@ -2330,7 +2744,8 @@ public class XmppConnectionService extends Service { return null; } - public Conversation find(final Iterable haystack, final Account account, final Jid jid) { + public Conversation find( + final Iterable haystack, final Account account, final Jid jid) { if (jid == null) { return null; } @@ -2361,15 +2776,27 @@ public class XmppConnectionService extends Service { return false; } - public Conversation findOrCreateConversation(Account account, Jid jid, boolean muc, final boolean async) { + public Conversation findOrCreateConversation( + Account account, Jid jid, boolean muc, final boolean async) { return this.findOrCreateConversation(account, jid, muc, false, async); } - public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final boolean joinAfterCreate, final boolean async) { + public Conversation findOrCreateConversation( + final Account account, + final Jid jid, + final boolean muc, + final boolean joinAfterCreate, + final boolean async) { return this.findOrCreateConversation(account, jid, muc, joinAfterCreate, null, async); } - public Conversation findOrCreateConversation(final Account account, final Jid jid, final boolean muc, final boolean joinAfterCreate, final MessageArchiveService.Query query, final boolean async) { + public Conversation findOrCreateConversation( + final Account account, + final Jid jid, + final boolean muc, + final boolean joinAfterCreate, + final MessageArchiveService.Query query, + final boolean async) { synchronized (this.conversations) { Conversation conversation = find(account, jid); if (conversation != null) { @@ -2398,38 +2825,45 @@ public class XmppConnectionService extends Service { conversationName = jid.getLocal(); } if (muc) { - conversation = new Conversation(conversationName, account, jid, - Conversation.MODE_MULTI); + conversation = + new Conversation( + conversationName, account, jid, Conversation.MODE_MULTI); } else { - conversation = new Conversation(conversationName, account, jid.asBareJid(), - Conversation.MODE_SINGLE); + conversation = + new Conversation( + conversationName, + account, + jid.asBareJid(), + Conversation.MODE_SINGLE); } this.databaseBackend.createConversation(conversation); loadMessagesFromDb = false; } final Conversation c = conversation; - final Runnable runnable = () -> { - if (loadMessagesFromDb) { - c.addAll(0, databaseBackend.getMessages(c, Config.PAGE_SIZE)); - updateConversationUi(); - c.messagesLoaded.set(true); - } - if (account.getXmppConnection() != null - && !c.getContact().isBlocked() - && account.getXmppConnection().getFeatures().mam() - && !muc) { - if (query == null) { - mMessageArchiveService.query(c); - } else { - if (query.getConversation() == null) { - mMessageArchiveService.query(c, query.getStart(), query.isCatchup()); + final Runnable runnable = + () -> { + if (loadMessagesFromDb) { + c.addAll(0, databaseBackend.getMessages(c, Config.PAGE_SIZE)); + updateConversationUi(); + c.messagesLoaded.set(true); } - } - } - if (joinAfterCreate) { - joinMuc(c); - } - }; + if (account.getXmppConnection() != null + && !c.getContact().isBlocked() + && account.getXmppConnection().getFeatures().mam() + && !muc) { + if (query == null) { + mMessageArchiveService.query(c); + } else { + if (query.getConversation() == null) { + mMessageArchiveService.query( + c, query.getStart(), query.isCatchup()); + } + } + } + if (joinAfterCreate) { + joinMuc(c); + } + }; if (async) { mDatabaseReaderExecutor.execute(runnable); } else { @@ -2445,7 +2879,8 @@ public class XmppConnectionService extends Service { archiveConversation(conversation, true); } - private void archiveConversation(Conversation conversation, final boolean maySynchronizeWithBookmarks) { + private void archiveConversation( + Conversation conversation, final boolean maySynchronizeWithBookmarks) { getNotificationService().clear(conversation); conversation.setStatus(Conversation.STATUS_ARCHIVED); conversation.setNextMessage(null); @@ -2467,7 +2902,9 @@ public class XmppConnectionService extends Service { } leaveMuc(conversation); } else { - if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { + if (conversation + .getContact() + .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { stopPresenceUpdatesTo(conversation.getContact()); } } @@ -2498,15 +2935,23 @@ public class XmppConnectionService extends Service { private void syncEnabledAccountSetting() { final boolean hasEnabledAccounts = hasEnabledAccounts(); - getPreferences().edit().putBoolean(SystemEventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts).apply(); + getPreferences() + .edit() + .putBoolean(SystemEventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts) + .apply(); toggleSetProfilePictureActivity(hasEnabledAccounts); } private void toggleSetProfilePictureActivity(final boolean enabled) { try { - final ComponentName name = new ComponentName(this, ChooseAccountForProfilePictureActivity.class); - final int targetState = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - getPackageManager().setComponentEnabledSetting(name, targetState, PackageManager.DONT_KILL_APP); + final ComponentName name = + new ComponentName(this, ChooseAccountForProfilePictureActivity.class); + final int targetState = + enabled + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + getPackageManager() + .setComponentEnabledSetting(name, targetState, PackageManager.DONT_KILL_APP); } catch (IllegalStateException e) { Log.d(Config.LOGTAG, "unable to toggle profile picture activity"); } @@ -2516,7 +2961,8 @@ public class XmppConnectionService extends Service { return this.unifiedPushBroker.reconfigurePushDistributor(); } - private Optional renewUnifiedPushEndpoints(final UnifiedPushBroker.PushTargetMessenger pushTargetMessenger) { + private Optional renewUnifiedPushEndpoints( + final UnifiedPushBroker.PushTargetMessenger pushTargetMessenger) { return this.unifiedPushBroker.renewUnifiedPushEndpoints(pushTargetMessenger); } @@ -2537,48 +2983,55 @@ public class XmppConnectionService extends Service { } public void createAccountFromKey(final String alias, final OnAccountCreated callback) { - new Thread(() -> { - try { - final X509Certificate[] chain = KeyChain.getCertificateChain(this, alias); - final X509Certificate cert = chain != null && chain.length > 0 ? chain[0] : null; - if (cert == null) { - callback.informUser(R.string.unable_to_parse_certificate); - return; - } - Pair info = CryptoHelper.extractJidAndName(cert); - if (info == null) { - callback.informUser(R.string.certificate_does_not_contain_jid); - return; - } - if (findAccountByJid(info.first) == null) { - final Account account = new Account(info.first, ""); - account.setPrivateKeyAlias(alias); - account.setOption(Account.OPTION_DISABLED, true); - account.setOption(Account.OPTION_FIXED_USERNAME, true); - account.setDisplayName(info.second); - createAccount(account); - callback.onAccountCreated(account); - if (Config.X509_VERIFICATION) { - try { - getMemorizingTrustManager().getNonInteractive(account.getServer()).checkClientTrusted(chain, "RSA"); - } catch (CertificateException e) { - callback.informUser(R.string.certificate_chain_is_not_trusted); - } - } - } else { - callback.informUser(R.string.account_already_exists); - } - } catch (Exception e) { - callback.informUser(R.string.unable_to_parse_certificate); - } - }).start(); - + new Thread( + () -> { + try { + final X509Certificate[] chain = + KeyChain.getCertificateChain(this, alias); + final X509Certificate cert = + chain != null && chain.length > 0 ? chain[0] : null; + if (cert == null) { + callback.informUser(R.string.unable_to_parse_certificate); + return; + } + Pair info = CryptoHelper.extractJidAndName(cert); + if (info == null) { + callback.informUser(R.string.certificate_does_not_contain_jid); + return; + } + if (findAccountByJid(info.first) == null) { + final Account account = new Account(info.first, ""); + account.setPrivateKeyAlias(alias); + account.setOption(Account.OPTION_DISABLED, true); + account.setOption(Account.OPTION_FIXED_USERNAME, true); + account.setDisplayName(info.second); + createAccount(account); + callback.onAccountCreated(account); + if (Config.X509_VERIFICATION) { + try { + getMemorizingTrustManager() + .getNonInteractive(account.getServer()) + .checkClientTrusted(chain, "RSA"); + } catch (CertificateException e) { + callback.informUser( + R.string.certificate_chain_is_not_trusted); + } + } + } else { + callback.informUser(R.string.account_already_exists); + } + } catch (Exception e) { + callback.informUser(R.string.unable_to_parse_certificate); + } + }) + .start(); } public void updateKeyInAccount(final Account account, final String alias) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": update key in account " + alias); try { - X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias); + X509Certificate[] chain = + KeyChain.getCertificateChain(XmppConnectionService.this, alias); Log.d(Config.LOGTAG, account.getJid().asBareJid() + " loaded certificate chain"); Pair info = CryptoHelper.extractJidAndName(chain[0]); if (info == null) { @@ -2591,7 +3044,9 @@ public class XmppConnectionService extends Service { databaseBackend.updateAccount(account); if (Config.X509_VERIFICATION) { try { - getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + getMemorizingTrustManager() + .getNonInteractive() + .checkClientTrusted(chain, "RSA"); } catch (CertificateException e) { showErrorToastInUi(R.string.certificate_chain_is_not_trusted); } @@ -2625,32 +3080,41 @@ public class XmppConnectionService extends Service { } } - public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) { + public void updateAccountPasswordOnServer( + final Account account, + final String newPassword, + final OnAccountPasswordChanged callback) { final Iq iq = getIqGenerator().generateSetPassword(account, newPassword); - sendIqPacket(account, iq, (packet) -> { - if (packet.getType() == Iq.Type.RESULT) { - account.setPassword(newPassword); - account.setOption(Account.OPTION_MAGIC_CREATE, false); - databaseBackend.updateAccount(account); - callback.onPasswordChangeSucceeded(); - } else { - callback.onPasswordChangeFailed(); - } - }); + sendIqPacket( + account, + iq, + (packet) -> { + if (packet.getType() == Iq.Type.RESULT) { + account.setPassword(newPassword); + account.setOption(Account.OPTION_MAGIC_CREATE, false); + databaseBackend.updateAccount(account); + callback.onPasswordChangeSucceeded(); + } else { + callback.onPasswordChangeFailed(); + } + }); } public void unregisterAccount(final Account account, final Consumer callback) { final Iq iqPacket = new Iq(Iq.Type.SET); - final Element query = iqPacket.addChild("query",Namespace.REGISTER); + final Element query = iqPacket.addChild("query", Namespace.REGISTER); query.addChild("remove"); - sendIqPacket(account, iqPacket, (response) -> { - if (response.getType() == Iq.Type.RESULT) { - deleteAccount(account); - callback.accept(true); - } else { - callback.accept(false); - } - }); + sendIqPacket( + account, + iqPacket, + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + deleteAccount(account); + callback.accept(true); + } else { + callback.accept(false); + } + }); } public void deleteAccount(final Account account) { @@ -2673,11 +3137,14 @@ public class XmppConnectionService extends Service { if (account.getXmppConnection() != null) { new Thread(() -> disconnect(account, !connected)).start(); } - final Runnable runnable = () -> { - if (!databaseBackend.deleteAccount(account)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to delete account"); - } - }; + final Runnable runnable = + () -> { + if (!databaseBackend.deleteAccount(account)) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": unable to delete account"); + } + }; mDatabaseWriterExecutor.execute(runnable); this.accounts.remove(account); if (CallIntegration.hasSystemFeature(this)) { @@ -2696,7 +3163,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnConversationUpdates.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as ConversationListChangedListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as ConversationListChangedListener"); } this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0); } @@ -2722,7 +3192,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnShowErrorToasts.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnShowErrorToastListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnShowErrorToastListener"); } } if (remainingListeners) { @@ -2746,7 +3219,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnAccountUpdates.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnAccountListChangedtListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnAccountListChangedtListener"); } } if (remainingListeners) { @@ -2770,7 +3246,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnCaptchaRequested.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnCaptchaRequestListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnCaptchaRequestListener"); } } if (remainingListeners) { @@ -2794,7 +3273,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnRosterUpdates.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnRosterUpdateListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnRosterUpdateListener"); } } if (remainingListeners) { @@ -2818,7 +3300,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnUpdateBlocklist.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnUpdateBlocklistListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnUpdateBlocklistListener"); } } if (remainingListeners) { @@ -2842,7 +3327,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnKeyStatusUpdated.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnKeyStatusUpdateListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnKeyStatusUpdateListener"); } } if (remainingListeners) { @@ -2866,7 +3354,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.onJingleRtpConnectionUpdate.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnJingleRtpConnectionUpdate"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnJingleRtpConnectionUpdate"); } } if (remainingListeners) { @@ -2890,7 +3381,10 @@ public class XmppConnectionService extends Service { synchronized (LISTENER_LOCK) { remainingListeners = checkListeners(); if (!this.mOnMucRosterUpdate.add(listener)) { - Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnMucRosterListener"); + Log.w( + Config.LOGTAG, + listener.getClass().getName() + + " is already registered as OnMucRosterListener"); } } if (remainingListeners) { @@ -2940,7 +3434,10 @@ public class XmppConnectionService extends Service { connection.sendActive(); } if (broadcastLastActivity) { - sendPresence(account, false); //send new presence but don't include idle because we are not + sendPresence( + account, + false); // send new presence but don't include idle because we are + // not } } } @@ -2976,7 +3473,8 @@ public class XmppConnectionService extends Service { public void connectMultiModeConversations(Account account) { List conversations = getConversations(); for (Conversation conversation : conversations) { - if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) { + if (conversation.getMode() == Conversation.MODE_MULTI + && conversation.getAccount() == account) { joinMuc(conversation); } } @@ -2986,13 +3484,19 @@ public class XmppConnectionService extends Service { final Account account = conversation.getAccount(); synchronized (account.inProgressConferenceJoins) { if (account.inProgressConferenceJoins.contains(conversation)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": canceling muc self ping because join is already under way"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": canceling muc self ping because join is already under way"); return; } } synchronized (account.inProgressConferencePings) { if (!account.inProgressConferencePings.add(conversation)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": canceling muc self ping because ping is already under way"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": canceling muc self ping because ping is already under way"); return; } } @@ -3000,23 +3504,45 @@ public class XmppConnectionService extends Service { final Iq ping = new Iq(Iq.Type.GET); ping.setTo(self); ping.addChild("ping", Namespace.PING); - sendIqPacket(conversation.getAccount(), ping, (response) -> { - if (response.getType() == Iq.Type.ERROR) { - final var error = response.getError(); - if (error == null || error.hasChild("service-unavailable") || error.hasChild("feature-not-implemented") || error.hasChild("item-not-found")) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ping to " + self + " came back as ignorable error"); - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ping to " + self + " failed. attempting rejoin"); - joinMuc(conversation); - } - } else if (response.getType() == Iq.Type.RESULT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ping to " + self + " came back fine"); - } - synchronized (account.inProgressConferencePings) { - account.inProgressConferencePings.remove(conversation); - } - }); + sendIqPacket( + conversation.getAccount(), + ping, + (response) -> { + if (response.getType() == Iq.Type.ERROR) { + final var error = response.getError(); + if (error == null + || error.hasChild("service-unavailable") + || error.hasChild("feature-not-implemented") + || error.hasChild("item-not-found")) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": ping to " + + self + + " came back as ignorable error"); + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": ping to " + + self + + " failed. attempting rejoin"); + joinMuc(conversation); + } + } else if (response.getType() == Iq.Type.RESULT) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": ping to " + + self + + " came back fine"); + } + synchronized (account.inProgressConferencePings) { + account.inProgressConferencePings.remove(conversation); + } + }); } + public void joinMuc(Conversation conversation) { joinMuc(conversation, null, false); } @@ -3029,7 +3555,10 @@ public class XmppConnectionService extends Service { joinMuc(conversation, onConferenceJoined, false); } - private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined, final boolean followedInvite) { + private void joinMuc( + Conversation conversation, + final OnConferenceJoined onConferenceJoined, + final boolean followedInvite) { final Account account = conversation.getAccount(); synchronized (account.pendingConferenceJoins) { account.pendingConferenceJoins.remove(conversation); @@ -3049,100 +3578,133 @@ public class XmppConnectionService extends Service { conversation.getMucOptions().flagNoAutoPushConfiguration(); } conversation.setHasMessagesLeftOnServer(false); - fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() { - - private void join(Conversation conversation) { - Account account = conversation.getAccount(); - final MucOptions mucOptions = conversation.getMucOptions(); - - if (mucOptions.nonanonymous() && !mucOptions.membersOnly() && !conversation.getBooleanAttribute("accept_non_anonymous", false)) { - synchronized (account.inProgressConferenceJoins) { - account.inProgressConferenceJoins.remove(conversation); - } - mucOptions.setError(MucOptions.Error.NON_ANONYMOUS); - updateConversationUi(); - if (onConferenceJoined != null) { - onConferenceJoined.onConferenceJoined(conversation); - } - return; - } + fetchConferenceConfiguration( + conversation, + new OnConferenceConfigurationFetched() { + + private void join(Conversation conversation) { + Account account = conversation.getAccount(); + final MucOptions mucOptions = conversation.getMucOptions(); + + if (mucOptions.nonanonymous() + && !mucOptions.membersOnly() + && !conversation.getBooleanAttribute( + "accept_non_anonymous", false)) { + synchronized (account.inProgressConferenceJoins) { + account.inProgressConferenceJoins.remove(conversation); + } + mucOptions.setError(MucOptions.Error.NON_ANONYMOUS); + updateConversationUi(); + if (onConferenceJoined != null) { + onConferenceJoined.onConferenceJoined(conversation); + } + return; + } - final Jid joinJid = mucOptions.getSelf().getFullJid(); - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": joining conversation " + joinJid.toString()); - final var packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous() || onConferenceJoined != null); - packet.setTo(joinJid); - Element x = packet.addChild("x", "http://jabber.org/protocol/muc"); - if (conversation.getMucOptions().getPassword() != null) { - x.addChild("password").setContent(mucOptions.getPassword()); - } + final Jid joinJid = mucOptions.getSelf().getFullJid(); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid().toString() + + ": joining conversation " + + joinJid.toString()); + final var packet = + mPresenceGenerator.selfPresence( + account, + Presence.Status.ONLINE, + mucOptions.nonanonymous() + || onConferenceJoined != null); + packet.setTo(joinJid); + Element x = packet.addChild("x", "http://jabber.org/protocol/muc"); + if (conversation.getMucOptions().getPassword() != null) { + x.addChild("password").setContent(mucOptions.getPassword()); + } - if (mucOptions.mamSupport()) { - // Use MAM instead of the limited muc history to get history - x.addChild("history").setAttribute("maxchars", "0"); - } else { - // Fallback to muc history - x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted().getTimestamp())); - } - sendPresencePacket(account, packet); - if (onConferenceJoined != null) { - onConferenceJoined.onConferenceJoined(conversation); - } - if (!joinJid.equals(conversation.getJid())) { - conversation.setContactJid(joinJid); - databaseBackend.updateConversation(conversation); - } + if (mucOptions.mamSupport()) { + // Use MAM instead of the limited muc history to get history + x.addChild("history").setAttribute("maxchars", "0"); + } else { + // Fallback to muc history + x.addChild("history") + .setAttribute( + "since", + PresenceGenerator.getTimestamp( + conversation + .getLastMessageTransmitted() + .getTimestamp())); + } + sendPresencePacket(account, packet); + if (onConferenceJoined != null) { + onConferenceJoined.onConferenceJoined(conversation); + } + if (!joinJid.equals(conversation.getJid())) { + conversation.setContactJid(joinJid); + databaseBackend.updateConversation(conversation); + } - if (mucOptions.mamSupport()) { - getMessageArchiveService().catchupMUC(conversation); - } - if (mucOptions.isPrivateAndNonAnonymous()) { - fetchConferenceMembers(conversation); - - if (followedInvite) { - final Bookmark bookmark = conversation.getBookmark(); - if (bookmark != null) { - if (!bookmark.autojoin()) { - bookmark.setAutojoin(true); - createBookmark(account, bookmark); + if (mucOptions.mamSupport()) { + getMessageArchiveService().catchupMUC(conversation); + } + if (mucOptions.isPrivateAndNonAnonymous()) { + fetchConferenceMembers(conversation); + + if (followedInvite) { + final Bookmark bookmark = conversation.getBookmark(); + if (bookmark != null) { + if (!bookmark.autojoin()) { + bookmark.setAutojoin(true); + createBookmark(account, bookmark); + } + } else { + saveConversationAsBookmark(conversation, null); + } } - } else { - saveConversationAsBookmark(conversation, null); + } + synchronized (account.inProgressConferenceJoins) { + account.inProgressConferenceJoins.remove(conversation); + sendUnsentMessages(conversation); } } - } - synchronized (account.inProgressConferenceJoins) { - account.inProgressConferenceJoins.remove(conversation); - sendUnsentMessages(conversation); - } - } - @Override - public void onConferenceConfigurationFetched(Conversation conversation) { - if (conversation.getStatus() == Conversation.STATUS_ARCHIVED) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": conversation (" + conversation.getJid() + ") got archived before IQ result"); - return; - } - join(conversation); - } + @Override + public void onConferenceConfigurationFetched(Conversation conversation) { + if (conversation.getStatus() == Conversation.STATUS_ARCHIVED) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": conversation (" + + conversation.getJid() + + ") got archived before IQ result"); + return; + } + join(conversation); + } - @Override - public void onFetchFailed(final Conversation conversation, final String errorCondition) { - if (conversation.getStatus() == Conversation.STATUS_ARCHIVED) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": conversation (" + conversation.getJid() + ") got archived before IQ result"); - return; - } - if ("remote-server-not-found".equals(errorCondition)) { - synchronized (account.inProgressConferenceJoins) { - account.inProgressConferenceJoins.remove(conversation); + @Override + public void onFetchFailed( + final Conversation conversation, final String errorCondition) { + if (conversation.getStatus() == Conversation.STATUS_ARCHIVED) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": conversation (" + + conversation.getJid() + + ") got archived before IQ result"); + return; + } + if ("remote-server-not-found".equals(errorCondition)) { + synchronized (account.inProgressConferenceJoins) { + account.inProgressConferenceJoins.remove(conversation); + } + conversation + .getMucOptions() + .setError(MucOptions.Error.SERVER_NOT_FOUND); + updateConversationUi(); + } else { + join(conversation); + fetchConferenceConfiguration(conversation); + } } - conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND); - updateConversationUi(); - } else { - join(conversation); - fetchConferenceConfiguration(conversation); - } - } - }); + }); updateConversationUi(); } else { synchronized (account.pendingConferenceJoins) { @@ -3158,65 +3720,88 @@ public class XmppConnectionService extends Service { final Account account = conversation.getAccount(); final AxolotlService axolotlService = account.getAxolotlService(); final String[] affiliations = {"member", "admin", "owner"}; - final Consumer callback = new Consumer() { + final Consumer callback = + new Consumer() { - private int i = 0; - private boolean success = true; + private int i = 0; + private boolean success = true; - @Override - public void accept(Iq response) { - final boolean omemoEnabled = conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL; - Element query = response.query("http://jabber.org/protocol/muc#admin"); - if (response.getType() == Iq.Type.RESULT && query != null) { - for (Element child : query.getChildren()) { - if ("item".equals(child.getName())) { - MucOptions.User user = AbstractParser.parseItem(conversation, child); - if (!user.realJidMatchesAccount()) { - boolean isNew = conversation.getMucOptions().updateUser(user); - Contact contact = user.getContact(); - if (omemoEnabled - && isNew - && user.getRealJid() != null - && (contact == null || !contact.mutualPresenceSubscription()) - && axolotlService.hasEmptyDeviceList(user.getRealJid())) { - axolotlService.fetchDeviceIds(user.getRealJid()); + @Override + public void accept(Iq response) { + final boolean omemoEnabled = + conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL; + Element query = response.query("http://jabber.org/protocol/muc#admin"); + if (response.getType() == Iq.Type.RESULT && query != null) { + for (Element child : query.getChildren()) { + if ("item".equals(child.getName())) { + MucOptions.User user = + AbstractParser.parseItem(conversation, child); + if (!user.realJidMatchesAccount()) { + boolean isNew = + conversation.getMucOptions().updateUser(user); + Contact contact = user.getContact(); + if (omemoEnabled + && isNew + && user.getRealJid() != null + && (contact == null + || !contact.mutualPresenceSubscription()) + && axolotlService.hasEmptyDeviceList( + user.getRealJid())) { + axolotlService.fetchDeviceIds(user.getRealJid()); + } + } } } + } else { + success = false; + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": could not request affiliation " + + affiliations[i] + + " in " + + conversation.getJid().asBareJid()); } - } - } else { - success = false; - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not request affiliation " + affiliations[i] + " in " + conversation.getJid().asBareJid()); - } - ++i; - if (i >= affiliations.length) { - List members = conversation.getMucOptions().getMembers(true); - if (success) { - List cryptoTargets = conversation.getAcceptedCryptoTargets(); - boolean changed = false; - for (ListIterator iterator = cryptoTargets.listIterator(); iterator.hasNext(); ) { - Jid jid = iterator.next(); - if (!members.contains(jid) && !members.contains(jid.getDomain())) { - iterator.remove(); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": removed " + jid + " from crypto targets of " + conversation.getName()); - changed = true; + ++i; + if (i >= affiliations.length) { + List members = conversation.getMucOptions().getMembers(true); + if (success) { + List cryptoTargets = conversation.getAcceptedCryptoTargets(); + boolean changed = false; + for (ListIterator iterator = cryptoTargets.listIterator(); + iterator.hasNext(); ) { + Jid jid = iterator.next(); + if (!members.contains(jid) + && !members.contains(jid.getDomain())) { + iterator.remove(); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": removed " + + jid + + " from crypto targets of " + + conversation.getName()); + changed = true; + } + } + if (changed) { + conversation.setAcceptedCryptoTargets(cryptoTargets); + updateConversation(conversation); + } } - } - if (changed) { - conversation.setAcceptedCryptoTargets(cryptoTargets); - updateConversation(conversation); + getAvatarService().clear(conversation); + updateMucRosterUi(); + updateConversationUi(); } } - getAvatarService().clear(conversation); - updateMucRosterUi(); - updateConversationUi(); - } - } - }; + }; for (String affiliation : affiliations) { - sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback); + sendIqPacket( + account, mIqGenerator.queryAffiliation(conversation, affiliation), callback); } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": fetching members for " + conversation.getName()); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": fetching members for " + conversation.getName()); } public void providePasswordForMuc(final Conversation conversation, final String password) { @@ -3254,47 +3839,72 @@ public class XmppConnectionService extends Service { private void deletePepNode(final Account account, final String node, final Runnable runnable) { final Iq request = mIqGenerator.deleteNode(node); - sendIqPacket(account, request, (packet) -> { - if (packet.getType() == Iq.Type.RESULT) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": successfully deleted pep node "+node); - if (runnable != null) { - runnable.run(); - } - } else { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": failed to delete "+ packet); - } - }); + sendIqPacket( + account, + request, + (packet) -> { + if (packet.getType() == Iq.Type.RESULT) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": successfully deleted pep node " + + node); + if (runnable != null) { + runnable.run(); + } + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": failed to delete " + packet); + } + }); } private void deleteVcardAvatar(final Account account, @NonNull final Runnable runnable) { final Iq retrieveVcard = mIqGenerator.retrieveVcardAvatar(account.getJid().asBareJid()); - sendIqPacket(account, retrieveVcard, (response) -> { - if (response.getType() != Iq.Type.RESULT) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": no vCard set. nothing to do"); - return; - } - final Element vcard = response.findChild("vCard", "vcard-temp"); - if (vcard == null) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": no vCard set. nothing to do"); - return; - } - Element photo = vcard.findChild("PHOTO"); - if (photo == null) { - photo = vcard.addChild("PHOTO"); - } - photo.clearChildren(); - final Iq publication = new Iq(Iq.Type.SET); - publication.setTo(account.getJid().asBareJid()); - publication.addChild(vcard); - sendIqPacket(account, publication, (publicationResponse) -> { - if (publicationResponse.getType() == Iq.Type.RESULT) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": successfully deleted vcard avatar"); - runnable.run(); - } else { - Log.d(Config.LOGTAG, "failed to publish vcard " + publicationResponse.getErrorCondition()); - } - }); - }); + sendIqPacket( + account, + retrieveVcard, + (response) -> { + if (response.getType() != Iq.Type.RESULT) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": no vCard set. nothing to do"); + return; + } + final Element vcard = response.findChild("vCard", "vcard-temp"); + if (vcard == null) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": no vCard set. nothing to do"); + return; + } + Element photo = vcard.findChild("PHOTO"); + if (photo == null) { + photo = vcard.addChild("PHOTO"); + } + photo.clearChildren(); + final Iq publication = new Iq(Iq.Type.SET); + publication.setTo(account.getJid().asBareJid()); + publication.addChild(vcard); + sendIqPacket( + account, + publication, + (publicationResponse) -> { + if (publicationResponse.getType() == Iq.Type.RESULT) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": successfully deleted vcard avatar"); + runnable.run(); + } else { + Log.d( + Config.LOGTAG, + "failed to publish vcard " + + publicationResponse.getErrorCondition()); + } + }); + }); } private boolean hasEnabledAccounts() { @@ -3309,18 +3919,32 @@ public class XmppConnectionService extends Service { return false; } - - public void getAttachments(final Conversation conversation, int limit, final OnMediaLoaded onMediaLoaded) { - getAttachments(conversation.getAccount(), conversation.getJid().asBareJid(), limit, onMediaLoaded); + public void getAttachments( + final Conversation conversation, int limit, final OnMediaLoaded onMediaLoaded) { + getAttachments( + conversation.getAccount(), conversation.getJid().asBareJid(), limit, onMediaLoaded); } - public void getAttachments(final Account account, final Jid jid, final int limit, final OnMediaLoaded onMediaLoaded) { + public void getAttachments( + final Account account, + final Jid jid, + final int limit, + final OnMediaLoaded onMediaLoaded) { getAttachments(account.getUuid(), jid.asBareJid(), limit, onMediaLoaded); } - - public void getAttachments(final String account, final Jid jid, final int limit, final OnMediaLoaded onMediaLoaded) { - new Thread(() -> onMediaLoaded.onMediaLoaded(fileBackend.convertToAttachments(databaseBackend.getRelativeFilePaths(account, jid, limit)))).start(); + public void getAttachments( + final String account, + final Jid jid, + final int limit, + final OnMediaLoaded onMediaLoaded) { + new Thread( + () -> + onMediaLoaded.onMediaLoaded( + fileBackend.convertToAttachments( + databaseBackend.getRelativeFilePaths( + account, jid, limit)))) + .start(); } public void persistSelfNick(final MucOptions.User self, final boolean modified) { @@ -3456,13 +4080,19 @@ public class XmppConnectionService extends Service { account.pendingConferenceLeaves.remove(conversation); } if (account.getStatus() == Account.State.ONLINE || now) { - sendPresencePacket(conversation.getAccount(), mPresenceGenerator.leave(conversation.getMucOptions())); + sendPresencePacket( + conversation.getAccount(), + mPresenceGenerator.leave(conversation.getMucOptions())); conversation.getMucOptions().setOffline(); Bookmark bookmark = conversation.getBookmark(); if (bookmark != null) { bookmark.setConversation(null); } - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": leaving muc " + conversation.getJid()); + Log.d( + Config.LOGTAG, + conversation.getAccount().getJid().asBareJid() + + ": leaving muc " + + conversation.getJid()); } else { synchronized (account.pendingConferenceLeaves) { account.pendingConferenceLeaves.add(conversation); @@ -3489,37 +4119,57 @@ public class XmppConnectionService extends Service { return null; } - - public void createPublicChannel(final Account account, final String name, final Jid address, final UiCallback callback) { - joinMuc(findOrCreateConversation(account, address, true, false, true), conversation -> { - final Bundle configuration = IqGenerator.defaultChannelConfiguration(); - if (!TextUtils.isEmpty(name)) { - configuration.putString("muc#roomconfig_roomname", name); - } - pushConferenceConfiguration(conversation, configuration, new OnConfigurationPushed() { - @Override - public void onPushSucceeded() { - saveConversationAsBookmark(conversation, name); - callback.success(conversation); - } - - @Override - public void onPushFailed() { - if (conversation.getMucOptions().getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER)) { - callback.error(R.string.unable_to_set_channel_configuration, conversation); - } else { - callback.error(R.string.joined_an_existing_channel, conversation); + public void createPublicChannel( + final Account account, + final String name, + final Jid address, + final UiCallback callback) { + joinMuc( + findOrCreateConversation(account, address, true, false, true), + conversation -> { + final Bundle configuration = IqGenerator.defaultChannelConfiguration(); + if (!TextUtils.isEmpty(name)) { + configuration.putString("muc#roomconfig_roomname", name); } - } - }); - }); + pushConferenceConfiguration( + conversation, + configuration, + new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + saveConversationAsBookmark(conversation, name); + callback.success(conversation); + } + + @Override + public void onPushFailed() { + if (conversation + .getMucOptions() + .getSelf() + .getAffiliation() + .ranks(MucOptions.Affiliation.OWNER)) { + callback.error( + R.string.unable_to_set_channel_configuration, + conversation); + } else { + callback.error( + R.string.joined_an_existing_channel, conversation); + } + } + }); + }); } - public boolean createAdhocConference(final Account account, - final String name, - final Iterable jids, - final UiCallback callback) { - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": creating adhoc conference with " + jids.toString()); + public boolean createAdhocConference( + final Account account, + final String name, + final Iterable jids, + final UiCallback callback) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid().toString() + + ": creating adhoc conference with " + + jids.toString()); if (account.getStatus() == Account.State.ONLINE) { try { String server = findConferenceServer(account); @@ -3527,44 +4177,61 @@ public class XmppConnectionService extends Service { if (callback != null) { callback.error(R.string.no_conference_server_found, null); } - return false; - } - final Jid jid = Jid.of(CryptoHelper.pronounceable(), server, null); - final Conversation conversation = findOrCreateConversation(account, jid, true, false, true); - joinMuc(conversation, new OnConferenceJoined() { - @Override - public void onConferenceJoined(final Conversation conversation) { - final Bundle configuration = IqGenerator.defaultGroupChatConfiguration(); - if (!TextUtils.isEmpty(name)) { - configuration.putString("muc#roomconfig_roomname", name); - } - pushConferenceConfiguration(conversation, configuration, new OnConfigurationPushed() { - @Override - public void onPushSucceeded() { - for (Jid invite : jids) { - invite(conversation, invite); - } - for (String resource : account.getSelfContact().getPresences().toResourceArray()) { - Jid other = account.getJid().withResource(resource); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending direct invite to " + other); - directInvite(conversation, other); - } - saveConversationAsBookmark(conversation, name); - if (callback != null) { - callback.success(conversation); - } - } - + return false; + } + final Jid jid = Jid.of(CryptoHelper.pronounceable(), server, null); + final Conversation conversation = + findOrCreateConversation(account, jid, true, false, true); + joinMuc( + conversation, + new OnConferenceJoined() { @Override - public void onPushFailed() { - archiveConversation(conversation); - if (callback != null) { - callback.error(R.string.conference_creation_failed, conversation); + public void onConferenceJoined(final Conversation conversation) { + final Bundle configuration = + IqGenerator.defaultGroupChatConfiguration(); + if (!TextUtils.isEmpty(name)) { + configuration.putString("muc#roomconfig_roomname", name); } + pushConferenceConfiguration( + conversation, + configuration, + new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + for (Jid invite : jids) { + invite(conversation, invite); + } + for (String resource : + account.getSelfContact() + .getPresences() + .toResourceArray()) { + Jid other = + account.getJid().withResource(resource); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": sending direct invite to " + + other); + directInvite(conversation, other); + } + saveConversationAsBookmark(conversation, name); + if (callback != null) { + callback.success(conversation); + } + } + + @Override + public void onPushFailed() { + archiveConversation(conversation); + if (callback != null) { + callback.error( + R.string.conference_creation_failed, + conversation); + } + } + }); } }); - } - }); return true; } catch (IllegalArgumentException e) { if (callback != null) { @@ -3584,75 +4251,121 @@ public class XmppConnectionService extends Service { fetchConferenceConfiguration(conversation, null); } - public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) { + public void fetchConferenceConfiguration( + final Conversation conversation, final OnConferenceConfigurationFetched callback) { final Iq request = mIqGenerator.queryDiscoInfo(conversation.getJid().asBareJid()); final var account = conversation.getAccount(); - sendIqPacket(account, request, response -> { - if (response.getType() == Iq.Type.RESULT) { - final MucOptions mucOptions = conversation.getMucOptions(); - final Bookmark bookmark = conversation.getBookmark(); - final boolean sameBefore = StringUtils.equals(bookmark == null ? null : bookmark.getBookmarkName(), mucOptions.getName()); - - if (mucOptions.updateConfiguration(new ServiceDiscoveryResult(response))) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": muc configuration changed for " + conversation.getJid().asBareJid()); - updateConversation(conversation); - } - - if (bookmark != null && (sameBefore || bookmark.getBookmarkName() == null)) { - if (bookmark.setBookmarkName(StringUtils.nullOnEmpty(mucOptions.getName()))) { - createBookmark(account, bookmark); - } - } - + sendIqPacket( + account, + request, + response -> { + if (response.getType() == Iq.Type.RESULT) { + final MucOptions mucOptions = conversation.getMucOptions(); + final Bookmark bookmark = conversation.getBookmark(); + final boolean sameBefore = + StringUtils.equals( + bookmark == null ? null : bookmark.getBookmarkName(), + mucOptions.getName()); + + if (mucOptions.updateConfiguration(new ServiceDiscoveryResult(response))) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": muc configuration changed for " + + conversation.getJid().asBareJid()); + updateConversation(conversation); + } - if (callback != null) { - callback.onConferenceConfigurationFetched(conversation); - } + if (bookmark != null + && (sameBefore || bookmark.getBookmarkName() == null)) { + if (bookmark.setBookmarkName( + StringUtils.nullOnEmpty(mucOptions.getName()))) { + createBookmark(account, bookmark); + } + } + if (callback != null) { + callback.onConferenceConfigurationFetched(conversation); + } - updateConversationUi(); - } else if (response.getType() == Iq.Type.TIMEOUT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received timeout waiting for conference configuration fetch"); - } else { - if (callback != null) { - callback.onFetchFailed(conversation, response.getErrorCondition()); - } - } - }); + updateConversationUi(); + } else if (response.getType() == Iq.Type.TIMEOUT) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": received timeout waiting for conference configuration" + + " fetch"); + } else { + if (callback != null) { + callback.onFetchFailed(conversation, response.getErrorCondition()); + } + } + }); } - public void pushNodeConfiguration(Account account, final String node, final Bundle options, final OnConfigurationPushed callback) { + public void pushNodeConfiguration( + Account account, + final String node, + final Bundle options, + final OnConfigurationPushed callback) { pushNodeConfiguration(account, account.getJid().asBareJid(), node, options, callback); } - public void pushNodeConfiguration(Account account, final Jid jid, final String node, final Bundle options, final OnConfigurationPushed callback) { + public void pushNodeConfiguration( + Account account, + final Jid jid, + final String node, + final Bundle options, + final OnConfigurationPushed callback) { Log.d(Config.LOGTAG, "pushing node configuration"); - sendIqPacket(account, mIqGenerator.requestPubsubConfiguration(jid, node), responseToRequest -> { - if (responseToRequest.getType() == Iq.Type.RESULT) { - Element pubsub = responseToRequest.findChild("pubsub", "http://jabber.org/protocol/pubsub#owner"); - Element configuration = pubsub == null ? null : pubsub.findChild("configure"); - Element x = configuration == null ? null : configuration.findChild("x", Namespace.DATA); - if (x != null) { - final Data data = Data.parse(x); - data.submit(options); - sendIqPacket(account, mIqGenerator.publishPubsubConfiguration(jid, node, data), responseToPublish -> { - if (responseToPublish.getType() == Iq.Type.RESULT && callback != null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": successfully changed node configuration for node " + node); - callback.onPushSucceeded(); - } else if (responseToPublish.getType() == Iq.Type.ERROR && callback != null) { + sendIqPacket( + account, + mIqGenerator.requestPubsubConfiguration(jid, node), + responseToRequest -> { + if (responseToRequest.getType() == Iq.Type.RESULT) { + Element pubsub = + responseToRequest.findChild( + "pubsub", "http://jabber.org/protocol/pubsub#owner"); + Element configuration = + pubsub == null ? null : pubsub.findChild("configure"); + Element x = + configuration == null + ? null + : configuration.findChild("x", Namespace.DATA); + if (x != null) { + final Data data = Data.parse(x); + data.submit(options); + sendIqPacket( + account, + mIqGenerator.publishPubsubConfiguration(jid, node, data), + responseToPublish -> { + if (responseToPublish.getType() == Iq.Type.RESULT + && callback != null) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": successfully changed node" + + " configuration for node " + + node); + callback.onPushSucceeded(); + } else if (responseToPublish.getType() == Iq.Type.ERROR + && callback != null) { + callback.onPushFailed(); + } + }); + } else if (callback != null) { callback.onPushFailed(); } - }); - } else if (callback != null) { - callback.onPushFailed(); - } - } else if (responseToRequest.getType() == Iq.Type.ERROR && callback != null) { - callback.onPushFailed(); - } - }); + } else if (responseToRequest.getType() == Iq.Type.ERROR && callback != null) { + callback.onPushFailed(); + } + }); } - public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConfigurationPushed callback) { + public void pushConferenceConfiguration( + final Conversation conversation, + final Bundle options, + final OnConfigurationPushed callback) { if (options.getString("muc#roomconfig_whois", "moderators").equals("anyone")) { conversation.setAttribute("accept_non_anonymous", true); updateConversation(conversation); @@ -3671,81 +4384,110 @@ public class XmppConnectionService extends Service { final Iq request = new Iq(Iq.Type.GET); request.setTo(conversation.getJid().asBareJid()); request.query("http://jabber.org/protocol/muc#owner"); - sendIqPacket(account, request, response -> { - if (response.getType() == Iq.Type.RESULT) { - final Data data = Data.parse(response.query().findChild("x", Namespace.DATA)); - data.submit(options); - final Iq set = new Iq(Iq.Type.SET); - set.setTo(conversation.getJid().asBareJid()); - set.query("http://jabber.org/protocol/muc#owner").addChild(data); - sendIqPacket(account, set, packet -> { - if (callback != null) { - if (packet.getType() == Iq.Type.RESULT) { - callback.onPushSucceeded(); - } else { - Log.d(Config.LOGTAG,"failed: "+packet.toString()); + sendIqPacket( + account, + request, + response -> { + if (response.getType() == Iq.Type.RESULT) { + final Data data = + Data.parse(response.query().findChild("x", Namespace.DATA)); + data.submit(options); + final Iq set = new Iq(Iq.Type.SET); + set.setTo(conversation.getJid().asBareJid()); + set.query("http://jabber.org/protocol/muc#owner").addChild(data); + sendIqPacket( + account, + set, + packet -> { + if (callback != null) { + if (packet.getType() == Iq.Type.RESULT) { + callback.onPushSucceeded(); + } else { + Log.d(Config.LOGTAG, "failed: " + packet.toString()); + callback.onPushFailed(); + } + } + }); + } else { + if (callback != null) { callback.onPushFailed(); } } }); - } else { - if (callback != null) { - callback.onPushFailed(); - } - } - }); } public void pushSubjectToConference(final Conversation conference, final String subject) { - final var packet = this.getMessageGenerator().conferenceSubject(conference, StringUtils.nullOnEmpty(subject)); + final var packet = + this.getMessageGenerator() + .conferenceSubject(conference, StringUtils.nullOnEmpty(subject)); this.sendMessagePacket(conference.getAccount(), packet); } - public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) { + public void changeAffiliationInConference( + final Conversation conference, + Jid user, + final MucOptions.Affiliation affiliation, + final OnAffiliationChanged callback) { final Jid jid = user.asBareJid(); - final Iq request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString()); - sendIqPacket(conference.getAccount(), request, (response) -> { - if (response.getType() == Iq.Type.RESULT) { - conference.getMucOptions().changeAffiliation(jid, affiliation); - getAvatarService().clear(conference); - if (callback != null) { - callback.onAffiliationChangedSuccessful(jid); - } else { - Log.d(Config.LOGTAG, "changed affiliation of " + user + " to " + affiliation); - } - } else if (callback != null) { - callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation); - } else { - Log.d(Config.LOGTAG, "unable to change affiliation"); - } - }); + final Iq request = + this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString()); + sendIqPacket( + conference.getAccount(), + request, + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + conference.getMucOptions().changeAffiliation(jid, affiliation); + getAvatarService().clear(conference); + if (callback != null) { + callback.onAffiliationChangedSuccessful(jid); + } else { + Log.d( + Config.LOGTAG, + "changed affiliation of " + user + " to " + affiliation); + } + } else if (callback != null) { + callback.onAffiliationChangeFailed( + jid, R.string.could_not_change_affiliation); + } else { + Log.d(Config.LOGTAG, "unable to change affiliation"); + } + }); } - public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role) { - final var account =conference.getAccount(); + public void changeRoleInConference( + final Conversation conference, final String nick, MucOptions.Role role) { + final var account = conference.getAccount(); final Iq request = this.mIqGenerator.changeRole(conference, nick, role.toString()); - sendIqPacket(account, request, (packet) -> { - if (packet.getType() != Iq.Type.RESULT) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + " unable to change role of " + nick); - } - }); + sendIqPacket( + account, + request, + (packet) -> { + if (packet.getType() != Iq.Type.RESULT) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + " unable to change role of " + nick); + } + }); } public void destroyRoom(final Conversation conversation, final OnRoomDestroy callback) { final Iq request = new Iq(Iq.Type.SET); request.setTo(conversation.getJid().asBareJid()); request.query("http://jabber.org/protocol/muc#owner").addChild("destroy"); - sendIqPacket(conversation.getAccount(), request, response -> { - if (response.getType() == Iq.Type.RESULT) { - if (callback != null) { - callback.onRoomDestroySucceeded(); - } - } else if (response.getType() == Iq.Type.ERROR) { - if (callback != null) { - callback.onRoomDestroyFailed(); - } - } - }); + sendIqPacket( + conversation.getAccount(), + request, + response -> { + if (response.getType() == Iq.Type.RESULT) { + if (callback != null) { + callback.onRoomDestroySucceeded(); + } + } else if (response.getType() == Iq.Type.ERROR) { + if (callback != null) { + callback.onRoomDestroyFailed(); + } + } + }); } private void disconnect(final Account account, boolean force) { @@ -3807,7 +4549,8 @@ public class XmppConnectionService extends Service { createContact(contact, autoGrant, null); } - public void createContact(final Contact contact, final boolean autoGrant, final String preAuth) { + public void createContact( + final Contact contact, final boolean autoGrant, final String preAuth) { if (autoGrant) { contact.setOption(Contact.Options.PREEMPTIVE_GRANT); contact.setOption(Contact.Options.ASKING); @@ -3825,9 +4568,9 @@ public class XmppConnectionService extends Service { final Account account = contact.getAccount(); if (account.getStatus() == Account.State.ONLINE) { final boolean ask = contact.getOption(Contact.Options.ASKING); - final boolean sendUpdates = contact - .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST) - && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); + final boolean sendUpdates = + contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST) + && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); final Iq iq = new Iq(Iq.Type.SET); iq.query(Namespace.ROSTER).addChild(contact.asElement()); account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); @@ -3835,86 +4578,114 @@ public class XmppConnectionService extends Service { sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); } if (ask) { - sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact, preAuth)); + sendPresencePacket( + account, mPresenceGenerator.requestPresenceUpdatesFrom(contact, preAuth)); } } else { syncRoster(contact.getAccount()); } } - public void publishMucAvatar(final Conversation conversation, final Uri image, final OnAvatarPublication callback) { - new Thread(() -> { - final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; - final int size = Config.AVATAR_SIZE; - final Avatar avatar = getFileBackend().getPepAvatar(image, size, format); - if (avatar != null) { - if (!getFileBackend().save(avatar)) { - callback.onAvatarPublicationFailed(R.string.error_saving_avatar); - return; - } - avatar.owner = conversation.getJid().asBareJid(); - publishMucAvatar(conversation, avatar, callback); - } else { - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_converting); - } - }).start(); - } - - public void publishAvatar(final Account account, final Uri image, final OnAvatarPublication callback) { - new Thread(() -> { - final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; - final int size = Config.AVATAR_SIZE; - final Avatar avatar = getFileBackend().getPepAvatar(image, size, format); - if (avatar != null) { - if (!getFileBackend().save(avatar)) { - Log.d(Config.LOGTAG, "unable to save vcard"); - callback.onAvatarPublicationFailed(R.string.error_saving_avatar); - return; - } - publishAvatar(account, avatar, callback); - } else { - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_converting); - } - }).start(); - + public void publishMucAvatar( + final Conversation conversation, final Uri image, final OnAvatarPublication callback) { + new Thread( + () -> { + final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; + final int size = Config.AVATAR_SIZE; + final Avatar avatar = + getFileBackend().getPepAvatar(image, size, format); + if (avatar != null) { + if (!getFileBackend().save(avatar)) { + callback.onAvatarPublicationFailed( + R.string.error_saving_avatar); + return; + } + avatar.owner = conversation.getJid().asBareJid(); + publishMucAvatar(conversation, avatar, callback); + } else { + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_converting); + } + }) + .start(); + } + + public void publishAvatar( + final Account account, final Uri image, final OnAvatarPublication callback) { + new Thread( + () -> { + final Bitmap.CompressFormat format = Config.AVATAR_FORMAT; + final int size = Config.AVATAR_SIZE; + final Avatar avatar = + getFileBackend().getPepAvatar(image, size, format); + if (avatar != null) { + if (!getFileBackend().save(avatar)) { + Log.d(Config.LOGTAG, "unable to save vcard"); + callback.onAvatarPublicationFailed( + R.string.error_saving_avatar); + return; + } + publishAvatar(account, avatar, callback); + } else { + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_converting); + } + }) + .start(); } - private void publishMucAvatar(Conversation conversation, Avatar avatar, OnAvatarPublication callback) { + private void publishMucAvatar( + Conversation conversation, Avatar avatar, OnAvatarPublication callback) { final var account = conversation.getAccount(); final Iq retrieve = mIqGenerator.retrieveVcardAvatar(avatar); - sendIqPacket(account, retrieve, (response) -> { - boolean itemNotFound = response.getType() == Iq.Type.ERROR && response.hasChild("error") && response.findChild("error").hasChild("item-not-found"); - if (response.getType() == Iq.Type.RESULT || itemNotFound) { - Element vcard = response.findChild("vCard", "vcard-temp"); - if (vcard == null) { - vcard = new Element("vCard", "vcard-temp"); - } - Element photo = vcard.findChild("PHOTO"); - if (photo == null) { - photo = vcard.addChild("PHOTO"); - } - photo.clearChildren(); - photo.addChild("TYPE").setContent(avatar.type); - photo.addChild("BINVAL").setContent(avatar.image); - final Iq publication = new Iq(Iq.Type.SET); - publication.setTo(conversation.getJid().asBareJid()); - publication.addChild(vcard); - sendIqPacket(account, publication, (publicationResponse) -> { - if (publicationResponse.getType() == Iq.Type.RESULT) { - callback.onAvatarPublicationSucceeded(); + sendIqPacket( + account, + retrieve, + (response) -> { + boolean itemNotFound = + response.getType() == Iq.Type.ERROR + && response.hasChild("error") + && response.findChild("error").hasChild("item-not-found"); + if (response.getType() == Iq.Type.RESULT || itemNotFound) { + Element vcard = response.findChild("vCard", "vcard-temp"); + if (vcard == null) { + vcard = new Element("vCard", "vcard-temp"); + } + Element photo = vcard.findChild("PHOTO"); + if (photo == null) { + photo = vcard.addChild("PHOTO"); + } + photo.clearChildren(); + photo.addChild("TYPE").setContent(avatar.type); + photo.addChild("BINVAL").setContent(avatar.image); + final Iq publication = new Iq(Iq.Type.SET); + publication.setTo(conversation.getJid().asBareJid()); + publication.addChild(vcard); + sendIqPacket( + account, + publication, + (publicationResponse) -> { + if (publicationResponse.getType() == Iq.Type.RESULT) { + callback.onAvatarPublicationSucceeded(); + } else { + Log.d( + Config.LOGTAG, + "failed to publish vcard " + + publicationResponse.getErrorCondition()); + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_server_reject); + } + }); } else { - Log.d(Config.LOGTAG, "failed to publish vcard " + publicationResponse.getErrorCondition()); - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_server_reject); + Log.d(Config.LOGTAG, "failed to request vcard " + response); + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_no_server_support); } }); - } else { - Log.d(Config.LOGTAG, "failed to request vcard " + response); - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_no_server_support); - } - }); } - public void publishAvatar(Account account, final Avatar avatar, final OnAvatarPublication callback) { + public void publishAvatar( + Account account, final Avatar avatar, final OnAvatarPublication callback) { final Bundle options; if (account.getXmppConnection().getFeatures().pepPublishOptions()) { options = PublishOptions.openAccess(); @@ -3924,118 +4695,193 @@ public class XmppConnectionService extends Service { publishAvatar(account, avatar, options, true, callback); } - public void publishAvatar(Account account, final Avatar avatar, final Bundle options, final boolean retry, final OnAvatarPublication callback) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": publishing avatar. options=" + options); + public void publishAvatar( + Account account, + final Avatar avatar, + final Bundle options, + final boolean retry, + final OnAvatarPublication callback) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + ": publishing avatar. options=" + options); final Iq packet = this.mIqGenerator.publishAvatar(avatar, options); - this.sendIqPacket(account, packet, result -> { - if (result.getType() == Iq.Type.RESULT) { - publishAvatarMetadata(account, avatar, options, true, callback); - } else if (retry && PublishOptions.preconditionNotMet(result)) { - pushNodeConfiguration(account, Namespace.AVATAR_DATA, options, new OnConfigurationPushed() { - @Override - public void onPushSucceeded() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": changed node configuration for avatar node"); - publishAvatar(account, avatar, options, false, callback); - } + this.sendIqPacket( + account, + packet, + result -> { + if (result.getType() == Iq.Type.RESULT) { + publishAvatarMetadata(account, avatar, options, true, callback); + } else if (retry && PublishOptions.preconditionNotMet(result)) { + pushNodeConfiguration( + account, + Namespace.AVATAR_DATA, + options, + new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": changed node configuration for avatar" + + " node"); + publishAvatar(account, avatar, options, false, callback); + } - @Override - public void onPushFailed() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to change node configuration for avatar node"); - publishAvatar(account, avatar, null, false, callback); + @Override + public void onPushFailed() { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": unable to change node configuration" + + " for avatar node"); + publishAvatar(account, avatar, null, false, callback); + } + }); + } else { + Element error = result.findChild("error"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": server rejected avatar " + + (avatar.size / 1024) + + "KiB " + + (error != null ? error.toString() : "")); + if (callback != null) { + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_server_reject); + } } }); - } else { - Element error = result.findChild("error"); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server rejected avatar " + (avatar.size / 1024) + "KiB " + (error != null ? error.toString() : "")); - if (callback != null) { - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_server_reject); - } - } - }); } - public void publishAvatarMetadata(Account account, final Avatar avatar, final Bundle options, final boolean retry, final OnAvatarPublication callback) { - final Iq packet = XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar, options); - sendIqPacket(account, packet, result -> { - if (result.getType() == Iq.Type.RESULT) { - if (account.setAvatar(avatar.getFilename())) { - getAvatarService().clear(account); - databaseBackend.updateAccount(account); - notifyAccountAvatarHasChanged(account); - } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": published avatar " + (avatar.size / 1024) + "KiB"); - if (callback != null) { - callback.onAvatarPublicationSucceeded(); - } - } else if (retry && PublishOptions.preconditionNotMet(result)) { - pushNodeConfiguration(account, Namespace.AVATAR_METADATA, options, new OnConfigurationPushed() { - @Override - public void onPushSucceeded() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": changed node configuration for avatar meta data node"); - publishAvatarMetadata(account, avatar, options, false, callback); - } + public void publishAvatarMetadata( + Account account, + final Avatar avatar, + final Bundle options, + final boolean retry, + final OnAvatarPublication callback) { + final Iq packet = + XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar, options); + sendIqPacket( + account, + packet, + result -> { + if (result.getType() == Iq.Type.RESULT) { + if (account.setAvatar(avatar.getFilename())) { + getAvatarService().clear(account); + databaseBackend.updateAccount(account); + notifyAccountAvatarHasChanged(account); + } + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": published avatar " + + (avatar.size / 1024) + + "KiB"); + if (callback != null) { + callback.onAvatarPublicationSucceeded(); + } + } else if (retry && PublishOptions.preconditionNotMet(result)) { + pushNodeConfiguration( + account, + Namespace.AVATAR_METADATA, + options, + new OnConfigurationPushed() { + @Override + public void onPushSucceeded() { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": changed node configuration for avatar" + + " meta data node"); + publishAvatarMetadata( + account, avatar, options, false, callback); + } - @Override - public void onPushFailed() { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to change node configuration for avatar meta data node"); - publishAvatarMetadata(account, avatar, null, false, callback); + @Override + public void onPushFailed() { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": unable to change node configuration" + + " for avatar meta data node"); + publishAvatarMetadata( + account, avatar, null, false, callback); + } + }); + } else { + if (callback != null) { + callback.onAvatarPublicationFailed( + R.string.error_publish_avatar_server_reject); + } } }); - } else { - if (callback != null) { - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_server_reject); - } - } - }); } public void republishAvatarIfNeeded(Account account) { if (account.getAxolotlService().isPepBroken()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping republication of avatar because pep is broken"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": skipping republication of avatar because pep is broken"); return; } final Iq packet = this.mIqGenerator.retrieveAvatarMetaData(null); - this.sendIqPacket(account, packet, new Consumer() { - - private Avatar parseAvatar(Iq packet) { - Element pubsub = packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); - if (pubsub != null) { - Element items = pubsub.findChild("items"); - if (items != null) { - return Avatar.parseMetadata(items); + this.sendIqPacket( + account, + packet, + new Consumer() { + + private Avatar parseAvatar(Iq packet) { + Element pubsub = + packet.findChild("pubsub", "http://jabber.org/protocol/pubsub"); + if (pubsub != null) { + Element items = pubsub.findChild("items"); + if (items != null) { + return Avatar.parseMetadata(items); + } + } + return null; } - } - return null; - } - private boolean errorIsItemNotFound(Iq packet) { - Element error = packet.findChild("error"); - return packet.getType() == Iq.Type.ERROR - && error != null - && error.hasChild("item-not-found"); - } + private boolean errorIsItemNotFound(Iq packet) { + Element error = packet.findChild("error"); + return packet.getType() == Iq.Type.ERROR + && error != null + && error.hasChild("item-not-found"); + } - @Override - public void accept(final Iq packet) { - if (packet.getType() == Iq.Type.RESULT || errorIsItemNotFound(packet)) { - Avatar serverAvatar = parseAvatar(packet); - if (serverAvatar == null && account.getAvatar() != null) { - Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar()); - if (avatar != null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": avatar on server was null. republishing"); - publishAvatar(account, fileBackend.getStoredPepAvatar(account.getAvatar()), null); - } else { - Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": error rereading avatar"); + @Override + public void accept(final Iq packet) { + if (packet.getType() == Iq.Type.RESULT || errorIsItemNotFound(packet)) { + Avatar serverAvatar = parseAvatar(packet); + if (serverAvatar == null && account.getAvatar() != null) { + Avatar avatar = fileBackend.getStoredPepAvatar(account.getAvatar()); + if (avatar != null) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": avatar on server was null. republishing"); + publishAvatar( + account, + fileBackend.getStoredPepAvatar(account.getAvatar()), + null); + } else { + Log.e( + Config.LOGTAG, + account.getJid().asBareJid() + + ": error rereading avatar"); + } + } } } - } - } - }); + }); } public void cancelAvatarFetches(final Account account) { synchronized (mInProgressAvatarFetches) { - for (final Iterator iterator = mInProgressAvatarFetches.iterator(); iterator.hasNext(); ) { + for (final Iterator iterator = mInProgressAvatarFetches.iterator(); + iterator.hasNext(); ) { final String KEY = iterator.next(); if (KEY.startsWith(account.getJid().asBareJid() + "_")) { iterator.remove(); @@ -4048,7 +4894,8 @@ public class XmppConnectionService extends Service { fetchAvatar(account, avatar, null); } - public void fetchAvatar(Account account, final Avatar avatar, final UiCallback callback) { + public void fetchAvatar( + Account account, final Avatar avatar, final UiCallback callback) { final String KEY = generateFetchKey(account, avatar); synchronized (this.mInProgressAvatarFetches) { if (mInProgressAvatarFetches.add(KEY)) { @@ -4065,159 +4912,208 @@ public class XmppConnectionService extends Service { } else if (avatar.origin == Avatar.Origin.PEP) { mOmittedPepAvatarFetches.add(KEY); } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": already fetching " + avatar.origin + " avatar for " + avatar.owner); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": already fetching " + + avatar.origin + + " avatar for " + + avatar.owner); } } } - private void fetchAvatarPep(final Account account, final Avatar avatar, final UiCallback callback) { + private void fetchAvatarPep( + final Account account, final Avatar avatar, final UiCallback callback) { final Iq packet = this.mIqGenerator.retrievePepAvatar(avatar); - sendIqPacket(account, packet, (result) -> { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); - } - final String ERROR = account.getJid().asBareJid() + ": fetching avatar for " + avatar.owner + " failed "; - if (result.getType() == Iq.Type.RESULT) { - avatar.image = IqParser.avatarData(result); - if (avatar.image != null) { - if (getFileBackend().save(avatar)) { - if (account.getJid().asBareJid().equals(avatar.owner)) { - if (account.setAvatar(avatar.getFilename())) { - databaseBackend.updateAccount(account); + sendIqPacket( + account, + packet, + (result) -> { + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); + } + final String ERROR = + account.getJid().asBareJid() + + ": fetching avatar for " + + avatar.owner + + " failed "; + if (result.getType() == Iq.Type.RESULT) { + avatar.image = IqParser.avatarData(result); + if (avatar.image != null) { + if (getFileBackend().save(avatar)) { + if (account.getJid().asBareJid().equals(avatar.owner)) { + if (account.setAvatar(avatar.getFilename())) { + databaseBackend.updateAccount(account); + } + getAvatarService().clear(account); + updateConversationUi(); + updateAccountUi(); + } else { + final Contact contact = + account.getRoster().getContact(avatar.owner); + contact.setAvatar(avatar); + syncRoster(account); + getAvatarService().clear(contact); + updateConversationUi(); + updateRosterUi(); + } + if (callback != null) { + callback.success(avatar); + } + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": successfully fetched pep avatar for " + + avatar.owner); + return; } - getAvatarService().clear(account); - updateConversationUi(); - updateAccountUi(); } else { - final Contact contact = account.getRoster().getContact(avatar.owner); - contact.setAvatar(avatar); - syncRoster(account); - getAvatarService().clear(contact); - updateConversationUi(); - updateRosterUi(); + + Log.d(Config.LOGTAG, ERROR + "(parsing error)"); } - if (callback != null) { - callback.success(avatar); + } else { + Element error = result.findChild("error"); + if (error == null) { + Log.d(Config.LOGTAG, ERROR + "(server error)"); + } else { + Log.d(Config.LOGTAG, ERROR + error.toString()); } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": successfully fetched pep avatar for " + avatar.owner); - return; } - } else { - - Log.d(Config.LOGTAG, ERROR + "(parsing error)"); - } - } else { - Element error = result.findChild("error"); - if (error == null) { - Log.d(Config.LOGTAG, ERROR + "(server error)"); - } else { - Log.d(Config.LOGTAG, ERROR + error.toString()); - } - } - if (callback != null) { - callback.error(0, null); - } - - }); + if (callback != null) { + callback.error(0, null); + } + }); } - private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback callback) { + private void fetchAvatarVcard( + final Account account, final Avatar avatar, final UiCallback callback) { final Iq packet = this.mIqGenerator.retrieveVcardAvatar(avatar); - this.sendIqPacket(account, packet, response -> { - final boolean previouslyOmittedPepFetch; - synchronized (mInProgressAvatarFetches) { - final String KEY = generateFetchKey(account, avatar); - mInProgressAvatarFetches.remove(KEY); - previouslyOmittedPepFetch = mOmittedPepAvatarFetches.remove(KEY); - } - if (response.getType() == Iq.Type.RESULT) { - Element vCard = response.findChild("vCard", "vcard-temp"); - Element photo = vCard != null ? vCard.findChild("PHOTO") : null; - String image = photo != null ? photo.findChildContent("BINVAL") : null; - if (image != null) { - avatar.image = image; - if (getFileBackend().save(avatar)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() - + ": successfully fetched vCard avatar for " + avatar.owner + " omittedPep=" + previouslyOmittedPepFetch); - if (avatar.owner.isBareJid()) { - if (account.getJid().asBareJid().equals(avatar.owner) && account.getAvatar() == null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": had no avatar. replacing with vcard"); - account.setAvatar(avatar.getFilename()); - databaseBackend.updateAccount(account); - getAvatarService().clear(account); - updateAccountUi(); - } else { - final Contact contact = account.getRoster().getContact(avatar.owner); - contact.setAvatar(avatar, previouslyOmittedPepFetch); - syncRoster(account); - getAvatarService().clear(contact); - updateRosterUi(); - } - updateConversationUi(); - } else { - Conversation conversation = find(account, avatar.owner.asBareJid()); - if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - MucOptions.User user = conversation.getMucOptions().findUserByFullJid(avatar.owner); - if (user != null) { - if (user.setAvatar(avatar)) { - getAvatarService().clear(user); - updateConversationUi(); - updateMucRosterUi(); - } - if (user.getRealJid() != null) { - Contact contact = account.getRoster().getContact(user.getRealJid()); - contact.setAvatar(avatar); + this.sendIqPacket( + account, + packet, + response -> { + final boolean previouslyOmittedPepFetch; + synchronized (mInProgressAvatarFetches) { + final String KEY = generateFetchKey(account, avatar); + mInProgressAvatarFetches.remove(KEY); + previouslyOmittedPepFetch = mOmittedPepAvatarFetches.remove(KEY); + } + if (response.getType() == Iq.Type.RESULT) { + Element vCard = response.findChild("vCard", "vcard-temp"); + Element photo = vCard != null ? vCard.findChild("PHOTO") : null; + String image = photo != null ? photo.findChildContent("BINVAL") : null; + if (image != null) { + avatar.image = image; + if (getFileBackend().save(avatar)) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": successfully fetched vCard avatar for " + + avatar.owner + + " omittedPep=" + + previouslyOmittedPepFetch); + if (avatar.owner.isBareJid()) { + if (account.getJid().asBareJid().equals(avatar.owner) + && account.getAvatar() == null) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": had no avatar. replacing with vcard"); + account.setAvatar(avatar.getFilename()); + databaseBackend.updateAccount(account); + getAvatarService().clear(account); + updateAccountUi(); + } else { + final Contact contact = + account.getRoster().getContact(avatar.owner); + contact.setAvatar(avatar, previouslyOmittedPepFetch); syncRoster(account); getAvatarService().clear(contact); updateRosterUi(); } + updateConversationUi(); + } else { + Conversation conversation = + find(account, avatar.owner.asBareJid()); + if (conversation != null + && conversation.getMode() == Conversation.MODE_MULTI) { + MucOptions.User user = + conversation + .getMucOptions() + .findUserByFullJid(avatar.owner); + if (user != null) { + if (user.setAvatar(avatar)) { + getAvatarService().clear(user); + updateConversationUi(); + updateMucRosterUi(); + } + if (user.getRealJid() != null) { + Contact contact = + account.getRoster() + .getContact(user.getRealJid()); + contact.setAvatar(avatar); + syncRoster(account); + getAvatarService().clear(contact); + updateRosterUi(); + } + } + } } } } } - } - } - }); + }); } public void checkForAvatar(final Account account, final UiCallback callback) { final Iq packet = this.mIqGenerator.retrieveAvatarMetaData(null); - this.sendIqPacket(account, packet, response -> { - if (response.getType() == Iq.Type.RESULT) { - Element pubsub = response.findChild("pubsub", "http://jabber.org/protocol/pubsub"); - if (pubsub != null) { - Element items = pubsub.findChild("items"); - if (items != null) { - Avatar avatar = Avatar.parseMetadata(items); - if (avatar != null) { - avatar.owner = account.getJid().asBareJid(); - if (fileBackend.isAvatarCached(avatar)) { - if (account.setAvatar(avatar.getFilename())) { - databaseBackend.updateAccount(account); + this.sendIqPacket( + account, + packet, + response -> { + if (response.getType() == Iq.Type.RESULT) { + Element pubsub = + response.findChild("pubsub", "http://jabber.org/protocol/pubsub"); + if (pubsub != null) { + Element items = pubsub.findChild("items"); + if (items != null) { + Avatar avatar = Avatar.parseMetadata(items); + if (avatar != null) { + avatar.owner = account.getJid().asBareJid(); + if (fileBackend.isAvatarCached(avatar)) { + if (account.setAvatar(avatar.getFilename())) { + databaseBackend.updateAccount(account); + } + getAvatarService().clear(account); + callback.success(avatar); + } else { + fetchAvatarPep(account, avatar, callback); + } + return; } - getAvatarService().clear(account); - callback.success(avatar); - } else { - fetchAvatarPep(account, avatar, callback); } - return; } } - } - } - callback.error(0, null); - }); + callback.error(0, null); + }); } public void notifyAccountAvatarHasChanged(final Account account) { final XmppConnection connection = account.getXmppConnection(); if (connection != null && connection.getFeatures().bookmarksConversion()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": avatar changed. resending presence to online group chats"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": avatar changed. resending presence to online group chats"); for (Conversation conversation : conversations) { - if (conversation.getAccount() == account && conversation.getMode() == Conversational.MODE_MULTI) { + if (conversation.getAccount() == account + && conversation.getMode() == Conversational.MODE_MULTI) { final MucOptions mucOptions = conversation.getMucOptions(); if (mucOptions.online()) { - final var packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, mucOptions.nonanonymous()); + final var packet = + mPresenceGenerator.selfPresence( + account, Presence.Status.ONLINE, mucOptions.nonanonymous()); packet.setTo(mucOptions.getSelf().getFullJid()); connection.sendPresencePacket(packet); } @@ -4244,7 +5140,8 @@ public class XmppConnectionService extends Service { mDatabaseWriterExecutor.execute(() -> databaseBackend.updateConversation(conversation)); } - private void reconnectAccount(final Account account, final boolean force, final boolean interactive) { + private void reconnectAccount( + final Account account, final boolean force, final boolean interactive) { synchronized (account) { final XmppConnection existingConnection = account.getXmppConnection(); final XmppConnection connection; @@ -4287,8 +5184,15 @@ public class XmppConnectionService extends Service { } public void invite(final Conversation conversation, final Jid contact) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": inviting " + contact + " to " + conversation.getJid().asBareJid()); - final MucOptions.User user = conversation.getMucOptions().findUserByRealJid(contact.asBareJid()); + Log.d( + Config.LOGTAG, + conversation.getAccount().getJid().asBareJid() + + ": inviting " + + contact + + " to " + + conversation.getJid().asBareJid()); + final MucOptions.User user = + conversation.getMucOptions().findUserByRealJid(contact.asBareJid()); if (user == null || user.getAffiliation() == MucOptions.Affiliation.OUTCAST) { changeAffiliationInConference(conversation, contact, MucOptions.Affiliation.NONE, null); } @@ -4304,21 +5208,29 @@ public class XmppConnectionService extends Service { public void resetSendingToWaiting(Account account) { for (Conversation conversation : getConversations()) { if (conversation.getAccount() == account) { - conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); + conversation.findUnsentTextMessages( + message -> markMessage(message, Message.STATUS_WAITING)); } } } - public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status) { + public Message markMessage( + final Account account, final Jid recipient, final String uuid, final int status) { return markMessage(account, recipient, uuid, status, null); } - public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status, String errorMessage) { + public Message markMessage( + final Account account, + final Jid recipient, + final String uuid, + final int status, + String errorMessage) { if (uuid == null) { return null; } for (Conversation conversation : getConversations()) { - if (conversation.getJid().asBareJid().equals(recipient) && conversation.getAccount() == account) { + if (conversation.getJid().asBareJid().equals(recipient) + && conversation.getAccount() == account) { final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid); if (message != null) { markMessage(message, status, errorMessage); @@ -4329,11 +5241,20 @@ public class XmppConnectionService extends Service { return null; } - public boolean markMessage(final Conversation conversation, final String uuid, final int status, final String serverMessageId) { + public boolean markMessage( + final Conversation conversation, + final String uuid, + final int status, + final String serverMessageId) { return markMessage(conversation, uuid, status, serverMessageId, null); } - public boolean markMessage(final Conversation conversation, final String uuid, final int status, final String serverMessageId, final LocalizedContent body) { + public boolean markMessage( + final Conversation conversation, + final String uuid, + final int status, + final String serverMessageId, + final LocalizedContent body) { if (uuid == null) { return false; } else { @@ -4371,14 +5292,19 @@ public class XmppConnectionService extends Service { markMessage(message, status, null); } - public void markMessage(final Message message, final int status, final String errorMessage) { markMessage(message, status, errorMessage, false); } - public void markMessage(final Message message, final int status, final String errorMessage, final boolean includeBody) { + public void markMessage( + final Message message, + final int status, + final String errorMessage, + final boolean includeBody) { final int oldStatus = message.getStatus(); - if (status == Message.STATUS_SEND_FAILED && (oldStatus == Message.STATUS_SEND_RECEIVED || oldStatus == Message.STATUS_SEND_DISPLAYED)) { + if (status == Message.STATUS_SEND_FAILED + && (oldStatus == Message.STATUS_SEND_RECEIVED + || oldStatus == Message.STATUS_SEND_DISPLAYED)) { return; } if (status == Message.STATUS_SEND_RECEIVED && oldStatus == Message.STATUS_SEND_DISPLAYED) { @@ -4398,7 +5324,10 @@ public class XmppConnectionService extends Service { } public long getAutomaticMessageDeletionDate() { - final long timeout = getLongPreference(AppSettings.AUTOMATIC_MESSAGE_DELETION, R.integer.automatic_message_deletion); + final long timeout = + getLongPreference( + AppSettings.AUTOMATIC_MESSAGE_DELETION, + R.integer.automatic_message_deletion); return timeout == 0 ? timeout : (System.currentTimeMillis() - (timeout * 1000)); } @@ -4428,11 +5357,14 @@ public class XmppConnectionService extends Service { } public boolean useTorToConnect() { - return QuickConversationsService.isConversations() && getBooleanPreference("use_tor", R.bool.use_tor); + return QuickConversationsService.isConversations() + && getBooleanPreference("use_tor", R.bool.use_tor); } public boolean showExtendedConnectionOptions() { - return QuickConversationsService.isConversations() && getBooleanPreference(AppSettings.SHOW_CONNECTION_OPTIONS, R.bool.show_connection_options); + return QuickConversationsService.isConversations() + && getBooleanPreference( + AppSettings.SHOW_CONNECTION_OPTIONS, R.bool.show_connection_options); } public boolean broadcastLastActivity() { @@ -4447,7 +5379,6 @@ public class XmppConnectionService extends Service { return count; } - private List threadSafeList(Set set) { synchronized (LISTENER_LOCK) { return set.isEmpty() ? Collections.emptyList() : new ArrayList<>(set); @@ -4466,14 +5397,22 @@ public class XmppConnectionService extends Service { } } - public void notifyJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) { - for (OnJingleRtpConnectionUpdate listener : threadSafeList(this.onJingleRtpConnectionUpdate)) { + public void notifyJingleRtpConnectionUpdate( + final Account account, + final Jid with, + final String sessionId, + final RtpEndUserState state) { + for (OnJingleRtpConnectionUpdate listener : + threadSafeList(this.onJingleRtpConnectionUpdate)) { listener.onJingleRtpConnectionUpdate(account, with, sessionId, state); } } - public void notifyJingleRtpConnectionUpdate(CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices) { - for (OnJingleRtpConnectionUpdate listener : threadSafeList(this.onJingleRtpConnectionUpdate)) { + public void notifyJingleRtpConnectionUpdate( + CallIntegration.AudioDevice selectedAudioDevice, + Set availableAudioDevices) { + for (OnJingleRtpConnectionUpdate listener : + threadSafeList(this.onJingleRtpConnectionUpdate)) { listener.onAudioDeviceChanged(selectedAudioDevice, availableAudioDevices); } } @@ -4493,8 +5432,12 @@ public class XmppConnectionService extends Service { public boolean displayCaptchaRequest(Account account, String id, Data data, Bitmap captcha) { if (mOnCaptchaRequested.size() > 0) { DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics(); - Bitmap scaled = Bitmap.createScaledBitmap(captcha, (int) (captcha.getWidth() * metrics.scaledDensity), - (int) (captcha.getHeight() * metrics.scaledDensity), false); + Bitmap scaled = + Bitmap.createScaledBitmap( + captcha, + (int) (captcha.getWidth() * metrics.scaledDensity), + (int) (captcha.getHeight() * metrics.scaledDensity), + false); for (OnCaptchaRequested listener : threadSafeList(this.mOnCaptchaRequested)) { listener.onCaptchaRequested(account, id, data, scaled); } @@ -4551,7 +5494,10 @@ public class XmppConnectionService extends Service { public Conversation findUniqueConversationByJid(XmppUri xmppUri) { List findings = new ArrayList<>(); for (Conversation c : getConversations()) { - if (c.getAccount().isEnabled() && c.getJid().asBareJid().equals(xmppUri.getJid()) && ((c.getMode() == Conversational.MODE_MULTI) == xmppUri.isAction(XmppUri.ACTION_JOIN))) { + if (c.getAccount().isEnabled() + && c.getJid().asBareJid().equals(xmppUri.getJid()) + && ((c.getMode() == Conversational.MODE_MULTI) + == xmppUri.isAction(XmppUri.ACTION_JOIN))) { findings.add(c); } } @@ -4566,17 +5512,19 @@ public class XmppConnectionService extends Service { markRead(conversation, null, true); } - public List markRead(final Conversation conversation, String upToUuid, boolean dismiss) { + public List markRead( + final Conversation conversation, String upToUuid, boolean dismiss) { if (dismiss) { mNotificationService.clear(conversation); } final List readMessages = conversation.markRead(upToUuid); if (readMessages.size() > 0) { - Runnable runnable = () -> { - for (Message message : readMessages) { - databaseBackend.updateMessage(message, false); - } - }; + Runnable runnable = + () -> { + for (Message message : readMessages) { + databaseBackend.updateMessage(message, false); + } + }; mDatabaseWriterExecutor.execute(runnable); updateConversationUi(); updateUnreadCountBadge(); @@ -4639,7 +5587,7 @@ public class XmppConnectionService extends Service { if (!last.isPrivateMessage()) { packet.setTo(packet.getTo().asBareJid()); } - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": server assisted "+packet); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": server assisted " + packet); this.sendMessagePacket(account, packet); } else { publishMds(last); @@ -4679,12 +5627,15 @@ public class XmppConnectionService extends Service { } else { itemId = conversation.getJid().asBareJid(); } - Log.d(Config.LOGTAG,"publishing mds for "+itemId+"/"+stanzaId); + Log.d(Config.LOGTAG, "publishing mds for " + itemId + "/" + stanzaId); publishMds(account, itemId, stanzaId, conversation); } private void publishMds( - final Account account, final Jid itemId, final String stanzaId, final Conversation conversation) { + final Account account, + final Jid itemId, + final String stanzaId, + final Conversation conversation) { final var item = mIqGenerator.mdsDisplayed(stanzaId, conversation); pushNodeAndEnforcePublishOptions( account, @@ -4792,7 +5743,10 @@ public class XmppConnectionService extends Service { } } if (Config.QUICKSY_DOMAIN != null) { - hosts.remove(Config.QUICKSY_DOMAIN.toEscapedString()); //we only want to show this when we type a e164 number + hosts.remove( + Config.QUICKSY_DOMAIN + .toEscapedString()); // we only want to show this when we type a e164 + // number } if (Config.MAGIC_CREATE_DOMAIN != null) { hosts.add(Config.MAGIC_CREATE_DOMAIN); @@ -4817,14 +5771,18 @@ public class XmppConnectionService extends Service { return mucServers; } - public void sendMessagePacket(final Account account, final im.conversations.android.xmpp.model.stanza.Message packet) { + public void sendMessagePacket( + final Account account, + final im.conversations.android.xmpp.model.stanza.Message packet) { final XmppConnection connection = account.getXmppConnection(); if (connection != null) { connection.sendMessagePacket(packet); } } - public void sendPresencePacket(final Account account, final im.conversations.android.xmpp.model.stanza.Presence packet) { + public void sendPresencePacket( + final Account account, + final im.conversations.android.xmpp.model.stanza.Presence packet) { final XmppConnection connection = account.getXmppConnection(); if (connection != null) { connection.sendPresencePacket(packet); @@ -4861,8 +5819,10 @@ public class XmppConnectionService extends Service { } final var packet = mPresenceGenerator.selfPresence(account, status); if (mLastActivity > 0 && includeIdleTimestamp) { - long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates - packet.addChild("idle", Namespace.IDLE).setAttribute("since", AbstractGenerator.getTimestamp(since)); + long since = + Math.min(mLastActivity, System.currentTimeMillis()); // don't send future dates + packet.addChild("idle", Namespace.IDLE) + .setAttribute("since", AbstractGenerator.getTimestamp(since)); } sendPresencePacket(account, packet); } @@ -4890,8 +5850,6 @@ public class XmppConnectionService extends Service { } } - - private void sendOfflinePresence(final Account account) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending offline presence"); sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account)); @@ -4929,7 +5887,8 @@ public class XmppConnectionService extends Service { ArrayList contacts = new ArrayList<>(); for (Account account : getAccounts()) { if ((account.isEnabled() || accountJid != null) - && (accountJid == null || accountJid.equals(account.getJid().asBareJid().toString()))) { + && (accountJid == null + || accountJid.equals(account.getJid().asBareJid().toString()))) { Contact contact = account.getRoster().getContactFromContactList(jid); if (contact != null) { contacts.add(contact); @@ -4941,7 +5900,9 @@ public class XmppConnectionService extends Service { public Conversation findFirstMuc(Jid jid) { for (Conversation conversation : getConversations()) { - if (conversation.getAccount().isEnabled() && conversation.getJid().asBareJid().equals(jid.asBareJid()) && conversation.getMode() == Conversation.MODE_MULTI) { + if (conversation.getAccount().isEnabled() + && conversation.getJid().asBareJid().equals(jid.asBareJid()) + && conversation.getMode() == Conversation.MODE_MULTI) { return conversation; } } @@ -4990,25 +5951,30 @@ public class XmppConnectionService extends Service { reference = null; } conversation.clearMessages(); - conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam + conversation.setHasMessagesLeftOnServer(false); // avoid messages getting loaded through mam conversation.setLastClearHistory(clearDate, reference); - Runnable runnable = () -> { - databaseBackend.deleteMessagesInConversation(conversation); - databaseBackend.updateConversation(conversation); - }; + Runnable runnable = + () -> { + databaseBackend.deleteMessagesInConversation(conversation); + databaseBackend.updateConversation(conversation); + }; mDatabaseWriterExecutor.execute(runnable); } - public boolean sendBlockRequest(final Blockable blockable, final boolean reportSpam, final String serverMsgId) { + public boolean sendBlockRequest( + final Blockable blockable, final boolean reportSpam, final String serverMsgId) { if (blockable != null && blockable.getBlockedJid() != null) { final var account = blockable.getAccount(); final Jid jid = blockable.getBlockedJid(); - this.sendIqPacket(account, getIqGenerator().generateSetBlockRequest(jid, reportSpam, serverMsgId), (response) -> { - if (response.getType() == Iq.Type.RESULT) { - account.getBlocklist().add(jid); - updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED); - } - }); + this.sendIqPacket( + account, + getIqGenerator().generateSetBlockRequest(jid, reportSpam, serverMsgId), + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + account.getBlocklist().add(jid); + updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED); + } + }); if (blockable.getBlockedJid().isFullJid()) { return false; } else if (removeBlockedConversations(blockable.getAccount(), jid)) { @@ -5027,15 +5993,24 @@ public class XmppConnectionService extends Service { synchronized (this.conversations) { boolean domainJid = blockedJid.getLocal() == null; for (Conversation conversation : this.conversations) { - boolean jidMatches = (domainJid && blockedJid.getDomain().equals(conversation.getJid().getDomain())) - || blockedJid.equals(conversation.getJid().asBareJid()); + boolean jidMatches = + (domainJid + && blockedJid + .getDomain() + .equals(conversation.getJid().getDomain())) + || blockedJid.equals(conversation.getJid().asBareJid()); if (conversation.getAccount() == account && conversation.getMode() == Conversation.MODE_SINGLE && jidMatches) { this.conversations.remove(conversation); markRead(conversation); conversation.setStatus(Conversation.STATUS_ARCHIVED); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving conversation " + conversation.getJid().asBareJid() + " because jid was blocked"); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": archiving conversation " + + conversation.getJid().asBareJid() + + " because jid was blocked"); updateConversation(conversation); removed = true; } @@ -5048,12 +6023,15 @@ public class XmppConnectionService extends Service { if (blockable != null && blockable.getJid() != null) { final var account = blockable.getAccount(); final Jid jid = blockable.getBlockedJid(); - this.sendIqPacket(account, getIqGenerator().generateSetUnblockRequest(jid), response -> { - if (response.getType() == Iq.Type.RESULT) { - account.getBlocklist().remove(jid); - updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED); - } - }); + this.sendIqPacket( + account, + getIqGenerator().generateSetUnblockRequest(jid), + response -> { + if (response.getType() == Iq.Type.RESULT) { + account.getBlocklist().remove(jid); + updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED); + } + }); } } @@ -5066,11 +6044,18 @@ public class XmppConnectionService extends Service { request = mIqGenerator.publishNick(displayName); } mAvatarService.clear(account); - sendIqPacket(account, request, (packet) -> { - if (packet.getType() == Iq.Type.ERROR) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to modify nick name " + packet); - } - }); + sendIqPacket( + account, + request, + (packet) -> { + if (packet.getType() == Iq.Type.ERROR) { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": unable to modify nick name " + + packet); + } + }); } public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair key) { @@ -5104,24 +6089,51 @@ public class XmppConnectionService extends Service { if (node != null && ver != null) { query.setAttribute("node", node + "#" + ver); } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid); - sendIqPacket(account, request, (response) -> { - if (response.getType() == Iq.Type.RESULT) { - final ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response); - if (presence.getVer().equals(discoveryResult.getVer())) { - databaseBackend.insertDiscoveryResult(discoveryResult); - injectServiceDiscoveryResult(account.getRoster(), presence.getHash(), presence.getVer(), discoveryResult); - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer()); - } - } else { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to fetch caps from " + jid); - } - }); + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": making disco request for " + + key.second + + " to " + + jid); + sendIqPacket( + account, + request, + (response) -> { + if (response.getType() == Iq.Type.RESULT) { + final ServiceDiscoveryResult discoveryResult = + new ServiceDiscoveryResult(response); + if (presence.getVer().equals(discoveryResult.getVer())) { + databaseBackend.insertDiscoveryResult(discoveryResult); + injectServiceDiscoveryResult( + account.getRoster(), + presence.getHash(), + presence.getVer(), + discoveryResult); + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": mismatch in caps for contact " + + jid + + " " + + presence.getVer() + + " vs " + + discoveryResult.getVer()); + } + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": unable to fetch caps from " + + jid); + } + }); } } - private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { + private void injectServiceDiscoveryResult( + Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { boolean rosterNeedsSync = false; for (final Contact contact : roster.getContacts()) { boolean serviceDiscoverySet = false; @@ -5144,14 +6156,17 @@ public class XmppConnectionService extends Service { final MessageArchiveService.Version version = MessageArchiveService.Version.get(account); final Iq request = new Iq(Iq.Type.GET); request.addChild("prefs", version.namespace); - sendIqPacket(account, request, (packet) -> { - final Element prefs = packet.findChild("prefs", version.namespace); - if (packet.getType() == Iq.Type.RESULT && prefs != null) { - callback.onPreferencesFetched(prefs); - } else { - callback.onPreferencesFetchFailed(); - } - }); + sendIqPacket( + account, + request, + (packet) -> { + final Element prefs = packet.findChild("prefs", version.namespace); + if (packet.getType() == Iq.Type.RESULT && prefs != null) { + callback.onPreferencesFetched(prefs); + } else { + callback.onPreferencesFetchFailed(); + } + }); } public PushManagementService getPushManagementService() { @@ -5200,11 +6215,13 @@ public class XmppConnectionService extends Service { for (XmppUri.Fingerprint fp : fingerprints) { if (fp.type == XmppUri.FingerprintType.OMEMO) { String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", ""); - FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint); + FingerprintStatus fingerprintStatus = + axolotlService.getFingerprintTrust(fingerprint); if (fingerprintStatus != null) { if (!fingerprintStatus.isVerified()) { performedVerification = true; - axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified()); + axolotlService.setFingerprintTrust( + fingerprint, fingerprintStatus.toVerified()); } } else { axolotlService.preVerifyFingerprint(contact, fingerprint); @@ -5221,10 +6238,12 @@ public class XmppConnectionService extends Service { if (fp.type == XmppUri.FingerprintType.OMEMO) { String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", ""); Log.d(Config.LOGTAG, "trying to verify own fp=" + fingerprint); - FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint); + FingerprintStatus fingerprintStatus = + axolotlService.getFingerprintTrust(fingerprint); if (fingerprintStatus != null) { if (!fingerprintStatus.isVerified()) { - axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified()); + axolotlService.setFingerprintTrust( + fingerprint, fingerprintStatus.toVerified()); verifiedSomething = true; } } else { @@ -5297,9 +6316,15 @@ public class XmppConnectionService extends Service { } public interface OnJingleRtpConnectionUpdate { - void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state); + void onJingleRtpConnectionUpdate( + final Account account, + final Jid with, + final String sessionId, + final RtpEndUserState state); - void onAudioDeviceChanged(CallIntegration.AudioDevice selectedAudioDevice, Set availableAudioDevices); + void onAudioDeviceChanged( + CallIntegration.AudioDevice selectedAudioDevice, + Set availableAudioDevices); } public interface OnAccountUpdate { @@ -5364,9 +6389,9 @@ public class XmppConnectionService extends Service { public void onReceive(final Context context, final Intent intent) { final String action = intent == null ? null : intent.getAction(); if (allowedActions.contains(action)) { - onStartCommand(intent,0,0); + onStartCommand(intent, 0, 0); } else { - Log.e(Config.LOGTAG,"restricting broadcast of event "+action); + Log.e(Config.LOGTAG, "restricting broadcast of event " + action); } } } @@ -5376,7 +6401,8 @@ public class XmppConnectionService extends Service { public final Set media; public final boolean reconnecting; - public OngoingCall(AbstractJingleConnection.Id id, Set media, final boolean reconnecting) { + public OngoingCall( + AbstractJingleConnection.Id id, Set media, final boolean reconnecting) { this.id = id; this.media = media; this.reconnecting = reconnecting; @@ -5387,7 +6413,9 @@ public class XmppConnectionService extends Service { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OngoingCall that = (OngoingCall) o; - return reconnecting == that.reconnecting && Objects.equal(id, that.id) && Objects.equal(media, that.media); + return reconnecting == that.reconnecting + && Objects.equal(id, that.id) + && Objects.equal(media, that.media); } @Override