From 4df8343b0534d0c2a7c0429d96bc8f9108e45de9 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 7 Oct 2019 09:51:03 +0200 Subject: [PATCH] set autojoin=true after following invite --- .../services/XmppConnectionService.java | 4812 +++++++++-------- .../ui/ChannelDiscoveryActivity.java | 6 +- .../conversations/xmpp/XmppConnection.java | 2 +- 3 files changed, 2414 insertions(+), 2406 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index a2b8ef7a7..aa1f768d8 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -297,7 +297,7 @@ public class XmppConnectionService extends Service { if (loggedInSuccessfully) { if (!TextUtils.isEmpty(account.getDisplayName())) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": display name wasn't empty on first log in. publishing"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": display name wasn't empty on first log in. publishing"); publishDisplayName(account); } } @@ -526,8 +526,8 @@ public class XmppConnectionService extends Service { message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_FILE); } - Log.d(Config.LOGTAG,"attachFile: type="+message.getType()); - Log.d(Config.LOGTAG,"counterpart="+message.getCounterpart()); + 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); if (runnable.isVideoMessage()) { mVideoCompressionExecutor.execute(runnable); @@ -558,7 +558,7 @@ public class XmppConnectionService extends Service { message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_IMAGE); } - Log.d(Config.LOGTAG,"attachImage: type="+message.getType()); + Log.d(Config.LOGTAG, "attachImage: type=" + message.getType()); mFileAddingExecutor.execute(() -> { try { getFileBackend().copyImageToPrivateStorage(message, uri); @@ -601,7 +601,7 @@ public class XmppConnectionService extends Service { final String action = intent == null ? null : intent.getAction(); final boolean needsForegroundService = intent != null && intent.getBooleanExtra(EventReceiver.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); } String pushedAccountHash = null; @@ -837,12 +837,12 @@ public class XmppConnectionService extends Service { } private void checkMucStillJoined(final Account account, final String hash, final String androidId) { - for(final Conversation conversation : this.conversations) { + for (final Conversation conversation : this.conversations) { if (conversation.getAccount() == account && conversation.getMode() == Conversational.MODE_MULTI) { Jid jid = conversation.getJid().asBareJid(); final String currentHash = CryptoHelper.getFingerprint(jid, androidId); if (currentHash.equals(hash)) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received cloud push notification for MUC "+jid); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received cloud push notification for MUC " + jid); return; } } @@ -1050,7 +1050,7 @@ public class XmppConnectionService extends Service { try { Security.insertProviderAt(Conscrypt.newProvider(), 1); } catch (Throwable throwable) { - Log.e(Config.LOGTAG,"unable to initialize security provider", throwable); + Log.e(Config.LOGTAG, "unable to initialize security provider", throwable); } Resolver.init(this); this.mRandom = new SecureRandom(); @@ -1137,7 +1137,7 @@ public class XmppConnectionService extends Service { final long start = SystemClock.elapsedRealtime(); final List relativeFilePaths = databaseBackend.getFilePathInfo(); final List changed = new ArrayList<>(); - for(final DatabaseBackend.FilePathInfo filePath : relativeFilePaths) { + for (final DatabaseBackend.FilePathInfo filePath : relativeFilePaths) { if (destroyed) { Log.d(Config.LOGTAG, "Stop checking for deleted files because service has been destroyed"); return; @@ -1148,7 +1148,7 @@ 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); @@ -1229,7 +1229,7 @@ public class XmppConnectionService extends Service { if (!mForceForegroundService.get()) { mNotificationService.dismissForcedForegroundNotification(); //if the channel was changed the previous call might fail } - Log.d(Config.LOGTAG,"ForegroundService: "+(status?"on":"off")); + Log.d(Config.LOGTAG, "ForegroundService: " + (status ? "on" : "off")); } public boolean foregroundNotificationNeedsUpdatingWhenErrorStateChanges() { @@ -1369,7 +1369,7 @@ public class XmppConnectionService extends Service { 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); } } @@ -1460,7 +1460,7 @@ public class XmppConnectionService extends Service { message.setBody(decryptedBody); message.setEncryption(Message.ENCRYPTION_DECRYPTED); if (!databaseBackend.updateMessage(message, message.getEditedId())) { - Log.e(Config.LOGTAG,"error updated message in DB after edit"); + Log.e(Config.LOGTAG, "error updated message in DB after edit"); } updateConversationUi(); return; @@ -1500,7 +1500,7 @@ public class XmppConnectionService extends Service { databaseBackend.createMessage(message); } else if (message.edited()) { if (!databaseBackend.updateMessage(message, message.getEditedId())) { - Log.e(Config.LOGTAG,"error updated message in DB after edit"); + Log.e(Config.LOGTAG, "error updated message in DB after edit"); } } updateConversationUi(); @@ -1526,7 +1526,7 @@ 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 { @@ -1586,7 +1586,7 @@ public class XmppConnectionService extends Service { }); } - public void processBookmarksInitial(Account account, Map bookmarks, final boolean pep) { + public void processBookmarksInitial(Account account, Map bookmarks, final boolean pep) { final Set previousBookmarks = account.getBookmarkedJids(); final boolean synchronizeWithBookmarks = synchronizeWithBookmarks(); for (Bookmark bookmark : bookmarks.values()) { @@ -1605,7 +1605,7 @@ public class XmppConnectionService extends Service { public void processDeletedBookmark(Account account, Jid jid) { final Conversation conversation = find(account, jid); if (conversation != null && conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": archiving destroyed conference ("+conversation.getJid()+") after receiving pep"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving destroyed conference (" + conversation.getJid() + ") after receiving pep"); archiveConversation(conversation, false); } } @@ -1619,7 +1619,7 @@ public class XmppConnectionService extends Service { } bookmark.setConversation(conversation); if (pep && synchronizeWithBookmarks && !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(); @@ -1627,7 +1627,7 @@ public class XmppConnectionService extends Service { final String current = mucOptions.getActualNick(); final String proposed = mucOptions.getProposedNick(); 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); } } @@ -1661,10 +1661,9 @@ public class XmppConnectionService extends Service { final XmppConnection connection = account.getXmppConnection(); if (connection.getFeatures().bookmarksConversion()) { IqPacket request = mIqGenerator.deleteItem(Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString()); - sendIqPacket(account, request, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - Log.d(Config.LOGTAG,packet.toString()); + sendIqPacket(account, request, (a, response) -> { + if (response.getType() == IqPacket.TYPE.ERROR) { + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": unable to delete bookmark " + response.getError()); } }); } else if (connection.getFeatures().bookmarksConversion()) { @@ -1691,7 +1690,7 @@ public class XmppConnectionService extends Service { for (Bookmark bookmark : account.getBookmarks()) { storage.addChild(bookmark); } - pushNodeAndEnforcePublishOptions(account,Namespace.BOOKMARKS,storage, PublishOptions.persistentWhitelistAccess()); + pushNodeAndEnforcePublishOptions(account, Namespace.BOOKMARKS, storage, PublishOptions.persistentWhitelistAccess()); } @@ -1705,7 +1704,7 @@ public class XmppConnectionService extends Service { } - 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 IqPacket packet = mIqGenerator.publishElement(node, element, id, options); sendIqPacket(account, packet, (a, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { @@ -1720,81 +1719,81 @@ public class XmppConnectionService extends Service { @Override public void onPushFailed() { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": unable to push node configuration ("+node+")"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to push node configuration (" + node + ")"); } }); } else { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": error publishing bookmarks (retry="+Boolean.toString(retry)+") "+response); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error publishing bookmarks (retry=" + Boolean.toString(retry) + ") " + response); } }); } - private void restoreFromDatabase() { - synchronized (this.conversations) { - final Map accountLookupTable = new Hashtable<>(); - for (Account account : this.accounts) { - accountLookupTable.put(account.getUuid(), account); - } - 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(); ) { - 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()); - iterator.remove(); - } - } - long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore; - Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms"); - Runnable runnable = () -> { - 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 (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(false); - 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 restoreFromDatabase() { + synchronized (this.conversations) { + final Map accountLookupTable = new Hashtable<>(); + for (Account account : this.accounts) { + accountLookupTable.put(account.getUuid(), account); + } + 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(); ) { + 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()); + iterator.remove(); + } + } + long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore; + Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms"); + Runnable runnable = () -> { + 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 (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(false); + 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.findUnreadMessages(message -> mNotificationService.pushFromBacklog(message)); - } + private void restoreMessages(Conversation conversation) { + conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); + conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); + conversation.findUnreadMessages(message -> mNotificationService.pushFromBacklog(message)); + } - public void loadPhoneContacts() { + public void loadPhoneContacts() { mContactMergerExecutor.execute(() -> { Map contacts = JabberIdContact.load(this); Log.d(Config.LOGTAG, "start merging phone contacts with roster"); @@ -1820,26 +1819,26 @@ public class XmppConnectionService extends Service { updateRosterUi(); mQuickConversationsService.considerSync(); }); - } + } - public void syncRoster(final Account account) { - mRosterSyncTaskManager.execute(account, () -> databaseBackend.writeRoster(account.getRoster())); - } + public void syncRoster(final Account account) { + mRosterSyncTaskManager.execute(account, () -> databaseBackend.writeRoster(account.getRoster())); + } - public List getConversations() { - return this.conversations; - } + public List getConversations() { + return this.conversations; + } - private void markFileDeleted(final String path) { + private void markFileDeleted(final String path) { final File file = new File(path); final boolean isInternalFile = fileBackend.isInternalFile(file); final List uuids = databaseBackend.markFileAsDeleted(file, isInternalFile); - Log.d(Config.LOGTAG, "deleted file " + path+" internal="+isInternalFile+", database hits="+uuids.size()); + Log.d(Config.LOGTAG, "deleted file " + path + " internal=" + isInternalFile + ", database hits=" + uuids.size()); markUuidsAsDeletedFiles(uuids); - } + } - private void markUuidsAsDeletedFiles(List uuids) { + private void markUuidsAsDeletedFiles(List uuids) { boolean deleted = false; for (Conversation conversation : getConversations()) { deleted |= conversation.markAsDeleted(uuids); @@ -1859,37 +1858,37 @@ public class XmppConnectionService extends Service { } } - public void populateWithOrderedConversations(final List list) { - populateWithOrderedConversations(list, true, true); - } + public void populateWithOrderedConversations(final List list) { + 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; } else { orderedUuids = new ArrayList<>(); - for(Conversation conversation : list) { + for (Conversation conversation : list) { orderedUuids.add(conversation.getUuid()); } } - list.clear(); - if (includeNoFileUpload) { - list.addAll(getConversations()); - } else { - for (Conversation conversation : getConversations()) { - if (conversation.getMode() == Conversation.MODE_SINGLE - || (conversation.getAccount().httpUploadAvailable() && conversation.getMucOptions().participating())) { - list.add(conversation); - } - } - } - try { - if (orderedUuids != null) { + list.clear(); + if (includeNoFileUpload) { + list.addAll(getConversations()); + } else { + for (Conversation conversation : getConversations()) { + if (conversation.getMode() == Conversation.MODE_SINGLE + || (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()); @@ -1901,361 +1900,361 @@ public class XmppConnectionService extends Service { } else { Collections.sort(list); } - } catch (IllegalArgumentException e) { - //ignore - } - } + } catch (IllegalArgumentException e) { + //ignore + } + } - 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); - } + 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); + } - } - } - }; - mDatabaseReaderExecutor.execute(runnable); - } + } + } + }; + mDatabaseReaderExecutor.execute(runnable); + } - public List getAccounts() { - return this.accounts; - } + public List getAccounts() { + 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) */ - public List findAllConferencesWith(Contact contact) { - ArrayList results = new ArrayList<>(); - for (final Conversation c : conversations) { - if (c.getMode() == Conversation.MODE_MULTI && (c.getJid().asBareJid().equals(contact.getJid().asBareJid()) || c.getMucOptions().isContactInRoom(contact))) { - results.add(c); - } - } - return results; - } - - public Conversation find(final Iterable haystack, final Contact contact) { - for (final Conversation conversation : haystack) { - if (conversation.getContact() == contact) { - return conversation; - } - } - return null; - } - - public Conversation find(final Iterable haystack, final Account account, final Jid jid) { - if (jid == null) { - return null; - } - for (final Conversation conversation : haystack) { - if ((account == null || conversation.getAccount() == account) - && (conversation.getJid().asBareJid().equals(jid.asBareJid()))) { - return conversation; - } - } - return null; - } - - public boolean isConversationsListEmpty(final Conversation ignore) { - synchronized (this.conversations) { - final int size = this.conversations.size(); - return size == 0 || size == 1 && this.conversations.get(0) == ignore; - } - } - - public boolean isConversationStillOpen(final Conversation conversation) { - synchronized (this.conversations) { - for (Conversation current : this.conversations) { - if (current == conversation) { - return true; - } - } - } - return false; - } - - 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) { - 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) { - synchronized (this.conversations) { - Conversation conversation = find(account, jid); - if (conversation != null) { - return conversation; - } - conversation = databaseBackend.findConversation(account, jid); - final boolean loadMessagesFromDb; - if (conversation != null) { - conversation.setStatus(Conversation.STATUS_AVAILABLE); - conversation.setAccount(account); - if (muc) { - conversation.setMode(Conversation.MODE_MULTI); - conversation.setContactJid(jid); - } else { - conversation.setMode(Conversation.MODE_SINGLE); - conversation.setContactJid(jid.asBareJid()); - } - databaseBackend.updateConversation(conversation); - loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true, false); - } else { - String conversationName; - Contact contact = account.getRoster().getContact(jid); - if (contact != null) { - conversationName = contact.getDisplayName(); - } else { - conversationName = jid.getLocal(); - } - if (muc) { - conversation = new Conversation(conversationName, account, jid, - Conversation.MODE_MULTI); - } else { - 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()); - } - } - } - if (joinAfterCreate) { - joinMuc(c); - } - }; - if (async) { - mDatabaseReaderExecutor.execute(runnable); - } else { - runnable.run(); - } - this.conversations.add(conversation); - updateConversationUi(); - return conversation; - } - } - - public void archiveConversation(Conversation conversation) { - archiveConversation(conversation, true); + public List findAllConferencesWith(Contact contact) { + ArrayList results = new ArrayList<>(); + for (final Conversation c : conversations) { + if (c.getMode() == Conversation.MODE_MULTI && (c.getJid().asBareJid().equals(contact.getJid().asBareJid()) || c.getMucOptions().isContactInRoom(contact))) { + results.add(c); + } + } + return results; } - private void archiveConversation(Conversation conversation, final boolean maySynchronizeWithBookmarks) { - getNotificationService().clear(conversation); - conversation.setStatus(Conversation.STATUS_ARCHIVED); - conversation.setNextMessage(null); - synchronized (this.conversations) { - getMessageArchiveService().kill(conversation); - if (conversation.getMode() == Conversation.MODE_MULTI) { - if (conversation.getAccount().getStatus() == Account.State.ONLINE) { - final Bookmark bookmark = conversation.getBookmark(); - if (maySynchronizeWithBookmarks && bookmark != null && synchronizeWithBookmarks()) { - if (conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) { - Account account = bookmark.getAccount(); - bookmark.setConversation(null); - deleteBookmark(account, bookmark); - } else if (bookmark.autojoin()) { - bookmark.setAutojoin(false); - createBookmark(bookmark.getAccount(), bookmark); - } - } - } + public Conversation find(final Iterable haystack, final Contact contact) { + for (final Conversation conversation : haystack) { + if (conversation.getContact() == contact) { + return conversation; + } + } + return null; + } + + public Conversation find(final Iterable haystack, final Account account, final Jid jid) { + if (jid == null) { + return null; + } + for (final Conversation conversation : haystack) { + if ((account == null || conversation.getAccount() == account) + && (conversation.getJid().asBareJid().equals(jid.asBareJid()))) { + return conversation; + } + } + return null; + } + + public boolean isConversationsListEmpty(final Conversation ignore) { + synchronized (this.conversations) { + final int size = this.conversations.size(); + return size == 0 || size == 1 && this.conversations.get(0) == ignore; + } + } + + public boolean isConversationStillOpen(final Conversation conversation) { + synchronized (this.conversations) { + for (Conversation current : this.conversations) { + if (current == conversation) { + return true; + } + } + } + return false; + } + + 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) { + 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) { + synchronized (this.conversations) { + Conversation conversation = find(account, jid); + if (conversation != null) { + return conversation; + } + conversation = databaseBackend.findConversation(account, jid); + final boolean loadMessagesFromDb; + if (conversation != null) { + conversation.setStatus(Conversation.STATUS_AVAILABLE); + conversation.setAccount(account); + if (muc) { + conversation.setMode(Conversation.MODE_MULTI); + conversation.setContactJid(jid); + } else { + conversation.setMode(Conversation.MODE_SINGLE); + conversation.setContactJid(jid.asBareJid()); + } + databaseBackend.updateConversation(conversation); + loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true, false); + } else { + String conversationName; + Contact contact = account.getRoster().getContact(jid); + if (contact != null) { + conversationName = contact.getDisplayName(); + } else { + conversationName = jid.getLocal(); + } + if (muc) { + conversation = new Conversation(conversationName, account, jid, + Conversation.MODE_MULTI); + } else { + 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()); + } + } + } + if (joinAfterCreate) { + joinMuc(c); + } + }; + if (async) { + mDatabaseReaderExecutor.execute(runnable); + } else { + runnable.run(); + } + this.conversations.add(conversation); + updateConversationUi(); + return conversation; + } + } + + public void archiveConversation(Conversation conversation) { + archiveConversation(conversation, true); + } + + private void archiveConversation(Conversation conversation, final boolean maySynchronizeWithBookmarks) { + getNotificationService().clear(conversation); + conversation.setStatus(Conversation.STATUS_ARCHIVED); + conversation.setNextMessage(null); + synchronized (this.conversations) { + getMessageArchiveService().kill(conversation); + if (conversation.getMode() == Conversation.MODE_MULTI) { + if (conversation.getAccount().getStatus() == Account.State.ONLINE) { + final Bookmark bookmark = conversation.getBookmark(); + if (maySynchronizeWithBookmarks && bookmark != null && synchronizeWithBookmarks()) { + if (conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) { + Account account = bookmark.getAccount(); + bookmark.setConversation(null); + deleteBookmark(account, bookmark); + } else if (bookmark.autojoin()) { + bookmark.setAutojoin(false); + createBookmark(bookmark.getAccount(), bookmark); + } + } + } if (conversation.getMucOptions().push()) { disableDirectMucPush(conversation); mPushManagementService.disablePushOnServer(conversation); } - leaveMuc(conversation); - } else { - if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { - stopPresenceUpdatesTo(conversation.getContact()); - } - } - updateConversation(conversation); - this.conversations.remove(conversation); - updateConversationUi(); - } - } + leaveMuc(conversation); + } else { + if (conversation.getContact().getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { + stopPresenceUpdatesTo(conversation.getContact()); + } + } + updateConversation(conversation); + this.conversations.remove(conversation); + updateConversationUi(); + } + } - public void stopPresenceUpdatesTo(Contact contact) { + public void stopPresenceUpdatesTo(Contact contact) { Log.d(Config.LOGTAG, "Canceling presence request from " + contact.getJid().toString()); sendPresencePacket(contact.getAccount(), mPresenceGenerator.stopPresenceUpdatesTo(contact)); contact.resetOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } - public void createAccount(final Account account) { - account.initAccountServices(this); - databaseBackend.createAccount(account); - this.accounts.add(account); - this.reconnectAccountInBackground(account); - updateAccountUi(); - syncEnabledAccountSetting(); - toggleForegroundService(); - } + public void createAccount(final Account account) { + account.initAccountServices(this); + databaseBackend.createAccount(account); + this.accounts.add(account); + this.reconnectAccountInBackground(account); + updateAccountUi(); + syncEnabledAccountSetting(); + toggleForegroundService(); + } - private void syncEnabledAccountSetting() { - final boolean hasEnabledAccounts = hasEnabledAccounts(); - getPreferences().edit().putBoolean(EventReceiver.SETTING_ENABLED_ACCOUNTS, hasEnabledAccounts).apply(); - toggleSetProfilePictureActivity(hasEnabledAccounts); - } + private void syncEnabledAccountSetting() { + final boolean hasEnabledAccounts = hasEnabledAccounts(); + getPreferences().edit().putBoolean(EventReceiver.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; + 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); } catch (IllegalStateException e) { - Log.d(Config.LOGTAG,"unable to toggle profile picture actvitiy"); + Log.d(Config.LOGTAG, "unable to toggle profile picture actvitiy"); } } - 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) { - Account account = new Account(info.first, ""); - account.setPrivateKeyAlias(alias); - account.setOption(Account.OPTION_DISABLED, true); - account.setDisplayName(info.second); - createAccount(account); - callback.onAccountCreated(account); - if (Config.X509_VERIFICATION) { - try { - getMemorizingTrustManager().getNonInteractive(account.getJid().getDomain()).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) { - e.printStackTrace(); - callback.informUser(R.string.unable_to_parse_certificate); - } - }).start(); + 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) { + Account account = new Account(info.first, ""); + account.setPrivateKeyAlias(alias); + account.setOption(Account.OPTION_DISABLED, true); + account.setDisplayName(info.second); + createAccount(account); + callback.onAccountCreated(account); + if (Config.X509_VERIFICATION) { + try { + getMemorizingTrustManager().getNonInteractive(account.getJid().getDomain()).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) { + e.printStackTrace(); + 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); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + " loaded certificate chain"); - Pair info = CryptoHelper.extractJidAndName(chain[0]); - if (info == null) { - showErrorToastInUi(R.string.certificate_does_not_contain_jid); - return; - } - if (account.getJid().asBareJid().equals(info.first)) { - account.setPrivateKeyAlias(alias); - account.setDisplayName(info.second); - databaseBackend.updateAccount(account); - if (Config.X509_VERIFICATION) { - try { - getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); - } catch (CertificateException e) { - showErrorToastInUi(R.string.certificate_chain_is_not_trusted); - } - account.getAxolotlService().regenerateKeys(true); - } - } else { - showErrorToastInUi(R.string.jid_does_not_match_certificate); - } - } catch (Exception e) { - e.printStackTrace(); - } - } + 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); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + " loaded certificate chain"); + Pair info = CryptoHelper.extractJidAndName(chain[0]); + if (info == null) { + showErrorToastInUi(R.string.certificate_does_not_contain_jid); + return; + } + if (account.getJid().asBareJid().equals(info.first)) { + account.setPrivateKeyAlias(alias); + account.setDisplayName(info.second); + databaseBackend.updateAccount(account); + if (Config.X509_VERIFICATION) { + try { + getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + } catch (CertificateException e) { + showErrorToastInUi(R.string.certificate_chain_is_not_trusted); + } + account.getAxolotlService().regenerateKeys(true); + } + } else { + showErrorToastInUi(R.string.jid_does_not_match_certificate); + } + } catch (Exception e) { + e.printStackTrace(); + } + } - public boolean updateAccount(final Account account) { - if (databaseBackend.updateAccount(account)) { - account.setShowErrorNotification(true); - this.statusListener.onStatusChanged(account); - databaseBackend.updateAccount(account); - reconnectAccountInBackground(account); - updateAccountUi(); - getNotificationService().updateErrorNotification(); - toggleForegroundService(); - syncEnabledAccountSetting(); - return true; - } else { - return false; - } - } + public boolean updateAccount(final Account account) { + if (databaseBackend.updateAccount(account)) { + account.setShowErrorNotification(true); + this.statusListener.onStatusChanged(account); + databaseBackend.updateAccount(account); + reconnectAccountInBackground(account); + updateAccountUi(); + getNotificationService().updateErrorNotification(); + toggleForegroundService(); + syncEnabledAccountSetting(); + return true; + } else { + return false; + } + } - public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) { - final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword); - sendIqPacket(account, iq, (a, packet) -> { - if (packet.getType() == IqPacket.TYPE.RESULT) { - a.setPassword(newPassword); - a.setOption(Account.OPTION_MAGIC_CREATE, false); - databaseBackend.updateAccount(a); - callback.onPasswordChangeSucceeded(); - } else { - callback.onPasswordChangeFailed(); - } - }); - } + public void updateAccountPasswordOnServer(final Account account, final String newPassword, final OnAccountPasswordChanged callback) { + final IqPacket iq = getIqGenerator().generateSetPassword(account, newPassword); + sendIqPacket(account, iq, (a, packet) -> { + if (packet.getType() == IqPacket.TYPE.RESULT) { + a.setPassword(newPassword); + a.setOption(Account.OPTION_MAGIC_CREATE, false); + databaseBackend.updateAccount(a); + callback.onPasswordChangeSucceeded(); + } else { + callback.onPasswordChangeFailed(); + } + }); + } - public void deleteAccount(final Account account) { - final boolean connected = account.getStatus() == Account.State.ONLINE; - synchronized (this.conversations) { - if (connected) { + public void deleteAccount(final Account account) { + final boolean connected = account.getStatus() == Account.State.ONLINE; + synchronized (this.conversations) { + if (connected) { account.getAxolotlService().deleteOmemoIdentity(); } for (final Conversation conversation : conversations) { @@ -2269,473 +2268,482 @@ public class XmppConnectionService extends Service { mNotificationService.clear(conversation); } } - 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"); - } - }; - mDatabaseWriterExecutor.execute(runnable); - this.accounts.remove(account); - this.mRosterSyncTaskManager.clear(account); - updateAccountUi(); - mNotificationService.updateErrorNotification(); - syncEnabledAccountSetting(); - toggleForegroundService(); - } - } + 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"); + } + }; + mDatabaseWriterExecutor.execute(runnable); + this.accounts.remove(account); + this.mRosterSyncTaskManager.clear(account); + updateAccountUi(); + mNotificationService.updateErrorNotification(); + syncEnabledAccountSetting(); + toggleForegroundService(); + } + } - public void setOnConversationListChangedListener(OnConversationUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnConversationUpdates.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as ConversationListChangedListener"); - } - this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0); - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnConversationListChangedListener(OnConversationUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnConversationUpdates.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as ConversationListChangedListener"); + } + this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0); + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnConversationListChangedListener(OnConversationUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnConversationUpdates.remove(listener); - this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnConversationListChangedListener(OnConversationUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnConversationUpdates.remove(listener); + this.mNotificationService.setIsInForeground(this.mOnConversationUpdates.size() > 0); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnShowErrorToastListener(OnShowErrorToast listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnShowErrorToasts.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnShowErrorToastListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnShowErrorToastListener(OnShowErrorToast listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnShowErrorToasts.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnShowErrorToastListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnShowErrorToasts.remove(onShowErrorToast); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnShowErrorToasts.remove(onShowErrorToast); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnAccountListChangedListener(OnAccountUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnAccountUpdates.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnAccountListChangedtListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnAccountListChangedListener(OnAccountUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnAccountUpdates.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnAccountListChangedtListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnAccountListChangedListener(OnAccountUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnAccountUpdates.remove(listener); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnAccountListChangedListener(OnAccountUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnAccountUpdates.remove(listener); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnCaptchaRequested.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnCaptchaRequestListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnCaptchaRequestedListener(OnCaptchaRequested listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnCaptchaRequested.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnCaptchaRequestListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnCaptchaRequestedListener(OnCaptchaRequested listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnCaptchaRequested.remove(listener); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnCaptchaRequestedListener(OnCaptchaRequested listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnCaptchaRequested.remove(listener); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnRosterUpdateListener(final OnRosterUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnRosterUpdates.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnRosterUpdateListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnRosterUpdateListener(final OnRosterUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnRosterUpdates.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnRosterUpdateListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnRosterUpdateListener(final OnRosterUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnRosterUpdates.remove(listener); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnRosterUpdateListener(final OnRosterUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnRosterUpdates.remove(listener); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnUpdateBlocklist.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnUpdateBlocklistListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnUpdateBlocklist.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnUpdateBlocklistListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnUpdateBlocklistListener(final OnUpdateBlocklist listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnUpdateBlocklist.remove(listener); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnUpdateBlocklistListener(final OnUpdateBlocklist listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnUpdateBlocklist.remove(listener); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnKeyStatusUpdated.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnKeyStatusUpdateListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnKeyStatusUpdatedListener(final OnKeyStatusUpdated listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnKeyStatusUpdated.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnKeyStatusUpdateListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnNewKeysAvailableListener(final OnKeyStatusUpdated listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnKeyStatusUpdated.remove(listener); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnNewKeysAvailableListener(final OnKeyStatusUpdated listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnKeyStatusUpdated.remove(listener); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - remainingListeners = checkListeners(); - if (!this.mOnMucRosterUpdate.add(listener)) { - Log.w(Config.LOGTAG,listener.getClass().getName()+" is already registered as OnMucRosterListener"); - } - } - if (remainingListeners) { - switchToForeground(); - } - } + public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + remainingListeners = checkListeners(); + if (!this.mOnMucRosterUpdate.add(listener)) { + Log.w(Config.LOGTAG, listener.getClass().getName() + " is already registered as OnMucRosterListener"); + } + } + if (remainingListeners) { + switchToForeground(); + } + } - public void removeOnMucRosterUpdateListener(final OnMucRosterUpdate listener) { - final boolean remainingListeners; - synchronized (LISTENER_LOCK) { - this.mOnMucRosterUpdate.remove(listener); - remainingListeners = checkListeners(); - } - if (remainingListeners) { - switchToBackground(); - } - } + public void removeOnMucRosterUpdateListener(final OnMucRosterUpdate listener) { + final boolean remainingListeners; + synchronized (LISTENER_LOCK) { + this.mOnMucRosterUpdate.remove(listener); + remainingListeners = checkListeners(); + } + if (remainingListeners) { + switchToBackground(); + } + } - public boolean checkListeners() { - return (this.mOnAccountUpdates.size() == 0 - && this.mOnConversationUpdates.size() == 0 - && this.mOnRosterUpdates.size() == 0 - && this.mOnCaptchaRequested.size() == 0 - && this.mOnMucRosterUpdate.size() == 0 - && this.mOnUpdateBlocklist.size() == 0 - && this.mOnShowErrorToasts.size() == 0 - && this.mOnKeyStatusUpdated.size() == 0); - } + public boolean checkListeners() { + return (this.mOnAccountUpdates.size() == 0 + && this.mOnConversationUpdates.size() == 0 + && this.mOnRosterUpdates.size() == 0 + && this.mOnCaptchaRequested.size() == 0 + && this.mOnMucRosterUpdate.size() == 0 + && this.mOnUpdateBlocklist.size() == 0 + && this.mOnShowErrorToasts.size() == 0 + && this.mOnKeyStatusUpdated.size() == 0); + } - private void switchToForeground() { - final boolean broadcastLastActivity = broadcastLastActivity(); - for (Conversation conversation : getConversations()) { - if (conversation.getMode() == Conversation.MODE_MULTI) { - conversation.getMucOptions().resetChatState(); - } else { - conversation.setIncomingChatState(Config.DEFAULT_CHATSTATE); - } - } - for (Account account : getAccounts()) { - if (account.getStatus() == Account.State.ONLINE) { - account.deactivateGracePeriod(); - final XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - if (connection.getFeatures().csi()) { - connection.sendActive(); - } - if (broadcastLastActivity) { - sendPresence(account, false); //send new presence but don't include idle because we are not - } - } - } - } - Log.d(Config.LOGTAG, "app switched into foreground"); - } + private void switchToForeground() { + final boolean broadcastLastActivity = broadcastLastActivity(); + for (Conversation conversation : getConversations()) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + conversation.getMucOptions().resetChatState(); + } else { + conversation.setIncomingChatState(Config.DEFAULT_CHATSTATE); + } + } + for (Account account : getAccounts()) { + if (account.getStatus() == Account.State.ONLINE) { + account.deactivateGracePeriod(); + final XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + if (connection.getFeatures().csi()) { + connection.sendActive(); + } + if (broadcastLastActivity) { + sendPresence(account, false); //send new presence but don't include idle because we are not + } + } + } + } + Log.d(Config.LOGTAG, "app switched into foreground"); + } - private void switchToBackground() { - final boolean broadcastLastActivity = broadcastLastActivity(); - if (broadcastLastActivity) { - mLastActivity = System.currentTimeMillis(); - final SharedPreferences.Editor editor = getPreferences().edit(); - editor.putLong(SETTING_LAST_ACTIVITY_TS, mLastActivity); - editor.apply(); - } - for (Account account : getAccounts()) { - if (account.getStatus() == Account.State.ONLINE) { - XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - if (broadcastLastActivity) { - sendPresence(account, true); - } - if (connection.getFeatures().csi()) { - connection.sendInactive(); - } - } - } - } - this.mNotificationService.setIsInForeground(false); - Log.d(Config.LOGTAG, "app switched into background"); - } + private void switchToBackground() { + final boolean broadcastLastActivity = broadcastLastActivity(); + if (broadcastLastActivity) { + mLastActivity = System.currentTimeMillis(); + final SharedPreferences.Editor editor = getPreferences().edit(); + editor.putLong(SETTING_LAST_ACTIVITY_TS, mLastActivity); + editor.apply(); + } + for (Account account : getAccounts()) { + if (account.getStatus() == Account.State.ONLINE) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + if (broadcastLastActivity) { + sendPresence(account, true); + } + if (connection.getFeatures().csi()) { + connection.sendInactive(); + } + } + } + } + this.mNotificationService.setIsInForeground(false); + Log.d(Config.LOGTAG, "app switched into background"); + } - private void connectMultiModeConversations(Account account) { - List conversations = getConversations(); - for (Conversation conversation : conversations) { - if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) { - joinMuc(conversation); - } - } - } + private void connectMultiModeConversations(Account account) { + List conversations = getConversations(); + for (Conversation conversation : conversations) { + if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) { + joinMuc(conversation); + } + } + } - public void mucSelfPingAndRejoin(final Conversation conversation) { - final Account account = conversation.getAccount(); - synchronized (account.inProgressConferenceJoins) { + public void mucSelfPingAndRejoin(final Conversation conversation) { + 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"); 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"); - return; + if (!account.inProgressConferencePings.add(conversation)) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": canceling muc self ping because ping is already under way"); + return; } } - final Jid self = conversation.getMucOptions().getSelf().getFullJid(); - final IqPacket ping = new IqPacket(IqPacket.TYPE.GET); - ping.setTo(self); - ping.addChild("ping", Namespace.PING); - sendIqPacket(conversation.getAccount(), ping, (a, response) -> { - if (response.getType() == IqPacket.TYPE.ERROR) { - Element error = response.findChild("error"); - if (error == null || error.hasChild("service-unavailable") || error.hasChild("feature-not-implemented") || error.hasChild("item-not-found")) { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": ping to "+self+" came back as ignorable error"); + final Jid self = conversation.getMucOptions().getSelf().getFullJid(); + final IqPacket ping = new IqPacket(IqPacket.TYPE.GET); + ping.setTo(self); + ping.addChild("ping", Namespace.PING); + sendIqPacket(conversation.getAccount(), ping, (a, response) -> { + if (response.getType() == IqPacket.TYPE.ERROR) { + Element error = response.findChild("error"); + if (error == null || error.hasChild("service-unavailable") || error.hasChild("feature-not-implemented") || error.hasChild("item-not-found")) { + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": ping to " + self + " came back as ignorable error"); } else { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": ping to "+self+" failed. attempting rejoin"); - joinMuc(conversation); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": ping to " + self + " failed. attempting rejoin"); + joinMuc(conversation); } } else if (response.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": ping to "+self+" came back fine"); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": ping to " + self + " came back fine"); } - synchronized (account.inProgressConferencePings) { - account.inProgressConferencePings.remove(conversation); + synchronized (account.inProgressConferencePings) { + account.inProgressConferencePings.remove(conversation); } }); } - public void joinMuc(Conversation conversation) { - joinMuc(conversation, null, false); - } + public void joinMuc(Conversation conversation) { + joinMuc(conversation, null, false); + } - public void joinMuc(Conversation conversation, boolean followedInvite) { - joinMuc(conversation, null, followedInvite); - } + public void joinMuc(Conversation conversation, boolean followedInvite) { + joinMuc(conversation, null, followedInvite); + } - private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) { - joinMuc(conversation, onConferenceJoined, false); - } + private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) { + joinMuc(conversation, onConferenceJoined, false); + } - private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined, final boolean followedInvite) { - final Account account = conversation.getAccount(); - synchronized (account.pendingConferenceJoins) { + private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined, final boolean followedInvite) { + final Account account = conversation.getAccount(); + synchronized (account.pendingConferenceJoins) { account.pendingConferenceJoins.remove(conversation); } synchronized (account.pendingConferenceLeaves) { account.pendingConferenceLeaves.remove(conversation); } - if (account.getStatus() == Account.State.ONLINE) { - synchronized (account.inProgressConferenceJoins) { + if (account.getStatus() == Account.State.ONLINE) { + synchronized (account.inProgressConferenceJoins) { account.inProgressConferenceJoins.add(conversation); } if (Config.MUC_LEAVE_BEFORE_JOIN) { sendPresencePacket(account, mPresenceGenerator.leave(conversation.getMucOptions())); } conversation.resetMucOptions(); - if (onConferenceJoined != null) { - conversation.getMucOptions().flagNoAutoPushConfiguration(); - } - conversation.setHasMessagesLeftOnServer(false); - fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() { + if (onConferenceJoined != null) { + conversation.getMucOptions().flagNoAutoPushConfiguration(); + } + conversation.setHasMessagesLeftOnServer(false); + fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() { - private void join(Conversation conversation) { - Account account = conversation.getAccount(); - final MucOptions mucOptions = conversation.getMucOptions(); + 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) { + 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(); + mucOptions.setError(MucOptions.Error.NON_ANONYMOUS); + updateConversationUi(); if (onConferenceJoined != null) { onConferenceJoined.onConferenceJoined(conversation); } - return; + return; } - final Jid joinJid = mucOptions.getSelf().getFullJid(); - Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": joining conversation " + joinJid.toString()); - PresencePacket 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()) { - getMessageArchiveService().catchupMUC(conversation); - } - if (mucOptions.isPrivateAndNonAnonymous()) { - fetchConferenceMembers(conversation); - if (followedInvite && conversation.getBookmark() == null) { - saveConversationAsBookmark(conversation, null); - } - } - if (mucOptions.push()) { - enableMucPush(conversation); + final Jid joinJid = mucOptions.getSelf().getFullJid(); + Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": joining conversation " + joinJid.toString()); + PresencePacket 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()); } - synchronized (account.inProgressConferenceJoins) { + + 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); + } + } else { + saveConversationAsBookmark(conversation, null); + } + } + } + if (mucOptions.push()) { + enableMucPush(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 onFetchFailed(final Conversation conversation, Element error) { + @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"); + 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, Element error) { + if (conversation.getStatus() == Conversation.STATUS_ARCHIVED) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": conversation (" + conversation.getJid() + ") got archived before IQ result"); return; } - if (error != null && "remote-server-not-found".equals(error.getName())) { - synchronized (account.inProgressConferenceJoins) { + if (error != null && "remote-server-not-found".equals(error.getName())) { + synchronized (account.inProgressConferenceJoins) { account.inProgressConferenceJoins.remove(conversation); } - conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND); - updateConversationUi(); - } else { - join(conversation); - fetchConferenceConfiguration(conversation); - } - } - }); - updateConversationUi(); - } else { - synchronized (account.pendingConferenceJoins) { + conversation.getMucOptions().setError(MucOptions.Error.SERVER_NOT_FOUND); + updateConversationUi(); + } else { + join(conversation); + fetchConferenceConfiguration(conversation); + } + } + }); + updateConversationUi(); + } else { + synchronized (account.pendingConferenceJoins) { account.pendingConferenceJoins.add(conversation); } - conversation.resetMucOptions(); - conversation.setHasMessagesLeftOnServer(false); - updateConversationUi(); - } - } + conversation.resetMucOptions(); + conversation.setHasMessagesLeftOnServer(false); + updateConversationUi(); + } + } - private void enableDirectMucPush(final Conversation conversation) { + private void enableDirectMucPush(final Conversation conversation) { final Account account = conversation.getAccount(); final Jid room = conversation.getJid().asBareJid(); final IqPacket enable = mIqGenerator.enablePush(conversation.getAccount().getJid(), conversation.getUuid(), null); enable.setTo(room); sendIqPacket(account, enable, (a, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": enabled direct push for muc "+room); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": enabled direct push for muc " + room); } else if (response.getType() == IqPacket.TYPE.ERROR) { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": unable to enable direct push for muc "+room+" "+response.getError()); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": unable to enable direct push for muc " + room + " " + response.getError()); } }); } - private void enableMucPush(final Conversation conversation) { - enableDirectMucPush(conversation); + private void enableMucPush(final Conversation conversation) { + enableDirectMucPush(conversation); mPushManagementService.registerPushTokenOnServer(conversation); } @@ -2746,231 +2754,231 @@ public class XmppConnectionService extends Service { disable.setTo(room); sendIqPacket(account, disable, (a, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": disabled direct push for muc "+room); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": disabled direct push for muc " + room); } else if (response.getType() == IqPacket.TYPE.ERROR) { - Log.d(Config.LOGTAG,a.getJid().asBareJid()+": unable to disable direct push for muc "+room+" "+response.getError()); + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": unable to disable direct push for muc " + room + " " + response.getError()); } }); } - private void fetchConferenceMembers(final Conversation conversation) { - final Account account = conversation.getAccount(); - final AxolotlService axolotlService = account.getAxolotlService(); - final String[] affiliations = {"member", "admin", "owner"}; - OnIqPacketReceived callback = new OnIqPacketReceived() { + private void fetchConferenceMembers(final Conversation conversation) { + final Account account = conversation.getAccount(); + final AxolotlService axolotlService = account.getAxolotlService(); + final String[] affiliations = {"member", "admin", "owner"}; + OnIqPacketReceived callback = new OnIqPacketReceived() { - private int i = 0; - private boolean success = true; + private int i = 0; + private boolean success = true; - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - final boolean omemoEnabled = conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL; - Element query = packet.query("http://jabber.org/protocol/muc#admin"); - if (packet.getType() == IqPacket.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()); - } - ++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.ofDomain(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); - } - } - getAvatarService().clear(conversation); - updateMucRosterUi(); - updateConversationUi(); - } - } - }; - for (String affiliation : affiliations) { - sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback); - } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": fetching members for " + conversation.getName()); - } + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + final boolean omemoEnabled = conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL; + Element query = packet.query("http://jabber.org/protocol/muc#admin"); + if (packet.getType() == IqPacket.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()); + } + ++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.ofDomain(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); + } + } + getAvatarService().clear(conversation); + updateMucRosterUi(); + updateConversationUi(); + } + } + }; + for (String affiliation : affiliations) { + sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback); + } + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": fetching members for " + conversation.getName()); + } - public void providePasswordForMuc(Conversation conversation, String password) { - if (conversation.getMode() == Conversation.MODE_MULTI) { - conversation.getMucOptions().setPassword(password); - if (conversation.getBookmark() != null) { - final Bookmark bookmark = conversation.getBookmark(); - if (synchronizeWithBookmarks()) { - bookmark.setAutojoin(true); - } - createBookmark(conversation.getAccount(), bookmark); - } - updateConversation(conversation); - joinMuc(conversation); - } - } + public void providePasswordForMuc(Conversation conversation, String password) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + conversation.getMucOptions().setPassword(password); + if (conversation.getBookmark() != null) { + final Bookmark bookmark = conversation.getBookmark(); + if (synchronizeWithBookmarks()) { + bookmark.setAutojoin(true); + } + createBookmark(conversation.getAccount(), bookmark); + } + updateConversation(conversation); + joinMuc(conversation); + } + } - private boolean hasEnabledAccounts() { - if (this.accounts == null) { - return false; - } - for (Account account : this.accounts) { - if (account.isEnabled()) { - return true; - } - } - return false; - } + private boolean hasEnabledAccounts() { + if (this.accounts == null) { + return false; + } + for (Account account : this.accounts) { + if (account.isEnabled()) { + return true; + } + } + return false; + } - public void getAttachments(final Conversation conversation, int limit, final OnMediaLoaded 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) { - getAttachments(account.getUuid(),jid.asBareJid(),limit, onMediaLoaded); + getAttachments(account.getUuid(), jid.asBareJid(), limit, onMediaLoaded); } - public void getAttachments(final String account, final Jid jid, final int limit, final OnMediaLoaded 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 persistSelfNick(MucOptions.User self) { - final Conversation conversation = self.getConversation(); - final boolean tookProposedNickFromBookmark = conversation.getMucOptions().isTookProposedNickFromBookmark(); - Jid full = self.getFullJid(); - if (!full.equals(conversation.getJid())) { - Log.d(Config.LOGTAG, "nick changed. updating"); - conversation.setContactJid(full); - databaseBackend.updateConversation(conversation); - } + public void persistSelfNick(MucOptions.User self) { + final Conversation conversation = self.getConversation(); + final boolean tookProposedNickFromBookmark = conversation.getMucOptions().isTookProposedNickFromBookmark(); + Jid full = self.getFullJid(); + if (!full.equals(conversation.getJid())) { + Log.d(Config.LOGTAG, "nick changed. updating"); + conversation.setContactJid(full); + databaseBackend.updateConversation(conversation); + } - final Bookmark bookmark = conversation.getBookmark(); - final String bookmarkedNick = bookmark == null ? null : bookmark.getNick(); + final Bookmark bookmark = conversation.getBookmark(); + final String bookmarkedNick = bookmark == null ? null : bookmark.getNick(); if (bookmark != null && (tookProposedNickFromBookmark || TextUtils.isEmpty(bookmarkedNick)) && !full.getResource().equals(bookmarkedNick)) { final Account account = conversation.getAccount(); final String defaultNick = MucOptions.defaultNick(account); if (TextUtils.isEmpty(bookmarkedNick) && full.getResource().equals(defaultNick)) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": do not overwrite empty bookmark nick with default nick for "+conversation.getJid().asBareJid()); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": do not overwrite empty bookmark nick with default nick for " + conversation.getJid().asBareJid()); return; } Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid()); bookmark.setNick(full.getResource()); createBookmark(bookmark.getAccount(), bookmark); } - } + } - public boolean renameInMuc(final Conversation conversation, final String nick, final UiCallback callback) { - final MucOptions options = conversation.getMucOptions(); - final Jid joinJid = options.createJoinJid(nick); - if (joinJid == null) { - return false; - } - if (options.online()) { - Account account = conversation.getAccount(); - options.setOnRenameListener(new OnRenameListener() { + public boolean renameInMuc(final Conversation conversation, final String nick, final UiCallback callback) { + final MucOptions options = conversation.getMucOptions(); + final Jid joinJid = options.createJoinJid(nick); + if (joinJid == null) { + return false; + } + if (options.online()) { + Account account = conversation.getAccount(); + options.setOnRenameListener(new OnRenameListener() { - @Override - public void onSuccess() { - callback.success(conversation); - } + @Override + public void onSuccess() { + callback.success(conversation); + } - @Override - public void onFailure() { - callback.error(R.string.nick_in_use, conversation); - } - }); + @Override + public void onFailure() { + callback.error(R.string.nick_in_use, conversation); + } + }); final PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, options.nonanonymous()); packet.setTo(joinJid); - sendPresencePacket(account, packet); - } else { - conversation.setContactJid(joinJid); - databaseBackend.updateConversation(conversation); - if (conversation.getAccount().getStatus() == Account.State.ONLINE) { - Bookmark bookmark = conversation.getBookmark(); - if (bookmark != null) { - bookmark.setNick(nick); - createBookmark(bookmark.getAccount(), bookmark); - } - joinMuc(conversation); - } - } - return true; - } + sendPresencePacket(account, packet); + } else { + conversation.setContactJid(joinJid); + databaseBackend.updateConversation(conversation); + if (conversation.getAccount().getStatus() == Account.State.ONLINE) { + Bookmark bookmark = conversation.getBookmark(); + if (bookmark != null) { + bookmark.setNick(nick); + createBookmark(bookmark.getAccount(), bookmark); + } + joinMuc(conversation); + } + } + return true; + } - public void leaveMuc(Conversation conversation) { - leaveMuc(conversation, false); - } + public void leaveMuc(Conversation conversation) { + leaveMuc(conversation, false); + } - private void leaveMuc(Conversation conversation, boolean now) { - final Account account = conversation.getAccount(); - synchronized (account.pendingConferenceJoins) { + private void leaveMuc(Conversation conversation, boolean now) { + final Account account = conversation.getAccount(); + synchronized (account.pendingConferenceJoins) { account.pendingConferenceJoins.remove(conversation); } synchronized (account.pendingConferenceLeaves) { account.pendingConferenceLeaves.remove(conversation); } - if (account.getStatus() == Account.State.ONLINE || now) { - 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()); - } else { - synchronized (account.pendingConferenceLeaves) { + if (account.getStatus() == Account.State.ONLINE || now) { + 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()); + } else { + synchronized (account.pendingConferenceLeaves) { account.pendingConferenceLeaves.add(conversation); } - } - } + } + } - public String findConferenceServer(final Account account) { - String server; - if (account.getXmppConnection() != null) { - server = account.getXmppConnection().getMucServer(); - if (server != null) { - return server; - } - } - for (Account other : getAccounts()) { - if (other != account && other.getXmppConnection() != null) { - server = other.getXmppConnection().getMucServer(); - if (server != null) { - return server; - } - } - } - return null; - } + public String findConferenceServer(final Account account) { + String server; + if (account.getXmppConnection() != null) { + server = account.getXmppConnection().getMucServer(); + if (server != null) { + return server; + } + } + for (Account other : getAccounts()) { + if (other != account && other.getXmppConnection() != null) { + server = other.getXmppConnection().getMucServer(); + if (server != null) { + return server; + } + } + } + return null; + } - public void createPublicChannel(final Account account, final String name, final Jid address, final UiCallback callback) { + 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)) { @@ -2995,83 +3003,83 @@ public class XmppConnectionService extends Service { }); } - 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); - if (server == null) { - if (callback != null) { - callback.error(R.string.no_conference_server_found, null); - } - return false; - } - final Jid jid = Jid.of(CryptoHelper.pronounceable(getRNG()), 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); + 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); + if (server == null) { + if (callback != null) { + callback.error(R.string.no_conference_server_found, null); + } + return false; + } + final Jid jid = Jid.of(CryptoHelper.pronounceable(getRNG()), 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); } - saveConversationAsBookmark(conversation, name); - if (callback != null) { - callback.success(conversation); - } - } + 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) { - callback.error(R.string.conference_creation_failed, null); - } - return false; - } - } else { - if (callback != null) { - callback.error(R.string.not_connected_try_again, null); - } - return false; - } - } + @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) { + callback.error(R.string.conference_creation_failed, null); + } + return false; + } + } else { + if (callback != null) { + callback.error(R.string.not_connected_try_again, null); + } + return false; + } + } - public void fetchConferenceConfiguration(final Conversation conversation) { - fetchConferenceConfiguration(conversation, null); - } + public void fetchConferenceConfiguration(final Conversation conversation) { + fetchConferenceConfiguration(conversation, null); + } - public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) { - IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.setTo(conversation.getJid().asBareJid()); - request.query("http://jabber.org/protocol/disco#info"); - sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { + public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) { + IqPacket request = new IqPacket(IqPacket.TYPE.GET); + request.setTo(conversation.getJid().asBareJid()); + request.query("http://jabber.org/protocol/disco#info"); + sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { final MucOptions mucOptions = conversation.getMucOptions(); final Bookmark bookmark = conversation.getBookmark(); final boolean sameBefore = StringUtils.equals(bookmark == null ? null : bookmark.getBookmarkName(), mucOptions.getName()); @@ -3095,133 +3103,133 @@ public class XmppConnectionService extends Service { updateConversationUi(); } else if (packet.getType() == IqPacket.TYPE.TIMEOUT) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received timeout waiting for conference configuration fetch"); - } else { - if (callback != null) { - callback.onFetchFailed(conversation, packet.getError()); - } - } - } - }); - } - - 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) { - Log.d(Config.LOGTAG,"pushing node configuration"); - sendIqPacket(account, mIqGenerator.requestPubsubConfiguration(jid, node), new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element pubsub = packet.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) { - Data data = Data.parse(x); - data.submit(options); - sendIqPacket(account, mIqGenerator.publishPubsubConfiguration(jid, node, data), new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT && callback != null) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": successfully changed node configuration for node "+node); - callback.onPushSucceeded(); - } else if (packet.getType() == IqPacket.TYPE.ERROR && callback != null) { - callback.onPushFailed(); - } - } - }); - } else if (callback != null) { - callback.onPushFailed(); - } - } else if (packet.getType() == IqPacket.TYPE.ERROR && callback != null) { - callback.onPushFailed(); - } - } - }); - } - - 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); - } - IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.setTo(conversation.getJid().asBareJid()); - request.query("http://jabber.org/protocol/muc#owner"); - sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Data data = Data.parse(packet.query().findChild("x", Namespace.DATA)); - data.submit(options); - IqPacket set = new IqPacket(IqPacket.TYPE.SET); - set.setTo(conversation.getJid().asBareJid()); - set.query("http://jabber.org/protocol/muc#owner").addChild(data); - sendIqPacket(account, set, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (callback != null) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - callback.onPushSucceeded(); - } else { - callback.onPushFailed(); - } - } - } - }); - } else { - if (callback != null) { - callback.onPushFailed(); - } - } - } - }); - } - - public void pushSubjectToConference(final Conversation conference, final String subject) { - MessagePacket 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) { - final Jid jid = user.asBareJid(); - IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString()); - sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - conference.getMucOptions().changeAffiliation(jid, affiliation); - getAvatarService().clear(conference); - callback.onAffiliationChangedSuccessful(jid); - } else { - callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation); - } - } - }); - } - - public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) { - List jids = new ArrayList<>(); - for (MucOptions.User user : conference.getMucOptions().getUsers()) { - if (user.getAffiliation() == before && user.getRealJid() != null) { - jids.add(user.getRealJid()); - } - } - IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString()); - sendIqPacket(conference.getAccount(), request, mDefaultIqHandler); - } - - public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role) { - IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString()); - Log.d(Config.LOGTAG, request.toString()); - sendIqPacket(conference.getAccount(), request, (account, packet) -> { - if (packet.getType() != IqPacket.TYPE.RESULT) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+" unable to change role of "+nick); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received timeout waiting for conference configuration fetch"); + } else { + if (callback != null) { + callback.onFetchFailed(conversation, packet.getError()); + } + } } }); - } + } + + 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) { + Log.d(Config.LOGTAG, "pushing node configuration"); + sendIqPacket(account, mIqGenerator.requestPubsubConfiguration(jid, node), new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element pubsub = packet.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) { + Data data = Data.parse(x); + data.submit(options); + sendIqPacket(account, mIqGenerator.publishPubsubConfiguration(jid, node, data), new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT && callback != null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": successfully changed node configuration for node " + node); + callback.onPushSucceeded(); + } else if (packet.getType() == IqPacket.TYPE.ERROR && callback != null) { + callback.onPushFailed(); + } + } + }); + } else if (callback != null) { + callback.onPushFailed(); + } + } else if (packet.getType() == IqPacket.TYPE.ERROR && callback != null) { + callback.onPushFailed(); + } + } + }); + } + + 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); + } + IqPacket request = new IqPacket(IqPacket.TYPE.GET); + request.setTo(conversation.getJid().asBareJid()); + request.query("http://jabber.org/protocol/muc#owner"); + sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Data data = Data.parse(packet.query().findChild("x", Namespace.DATA)); + data.submit(options); + IqPacket set = new IqPacket(IqPacket.TYPE.SET); + set.setTo(conversation.getJid().asBareJid()); + set.query("http://jabber.org/protocol/muc#owner").addChild(data); + sendIqPacket(account, set, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (callback != null) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + callback.onPushSucceeded(); + } else { + callback.onPushFailed(); + } + } + } + }); + } else { + if (callback != null) { + callback.onPushFailed(); + } + } + } + }); + } + + public void pushSubjectToConference(final Conversation conference, final String subject) { + MessagePacket 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) { + final Jid jid = user.asBareJid(); + IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString()); + sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + conference.getMucOptions().changeAffiliation(jid, affiliation); + getAvatarService().clear(conference); + callback.onAffiliationChangedSuccessful(jid); + } else { + callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation); + } + } + }); + } + + public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) { + List jids = new ArrayList<>(); + for (MucOptions.User user : conference.getMucOptions().getUsers()) { + if (user.getAffiliation() == before && user.getRealJid() != null) { + jids.add(user.getRealJid()); + } + } + IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString()); + sendIqPacket(conference.getAccount(), request, mDefaultIqHandler); + } + + public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role) { + IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString()); + Log.d(Config.LOGTAG, request.toString()); + sendIqPacket(conference.getAccount(), request, (account, packet) -> { + if (packet.getType() != IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + " unable to change role of " + nick); + } + }); + } public void destroyRoom(final Conversation conversation, final OnRoomDestroy callback) { IqPacket request = new IqPacket(IqPacket.TYPE.SET); @@ -3243,158 +3251,158 @@ public class XmppConnectionService extends Service { }); } - private void disconnect(Account account, boolean force) { - if ((account.getStatus() == Account.State.ONLINE) - || (account.getStatus() == Account.State.DISABLED)) { - final XmppConnection connection = account.getXmppConnection(); - if (!force) { - List conversations = getConversations(); - for (Conversation conversation : conversations) { - if (conversation.getAccount() == account) { - if (conversation.getMode() == Conversation.MODE_MULTI) { - leaveMuc(conversation, true); - } - } - } - sendOfflinePresence(account); - } - connection.disconnect(force); - } - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - public void updateMessage(Message message) { - updateMessage(message, true); - } - - public void updateMessage(Message message, boolean includeBody) { - databaseBackend.updateMessage(message, includeBody); - updateConversationUi(); - } - - public void updateMessage(Message message, String uuid) { - if (!databaseBackend.updateMessage(message, uuid)) { - Log.e(Config.LOGTAG,"error updated message in DB after edit"); + private void disconnect(Account account, boolean force) { + if ((account.getStatus() == Account.State.ONLINE) + || (account.getStatus() == Account.State.DISABLED)) { + final XmppConnection connection = account.getXmppConnection(); + if (!force) { + List conversations = getConversations(); + for (Conversation conversation : conversations) { + if (conversation.getAccount() == account) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + leaveMuc(conversation, true); + } + } + } + sendOfflinePresence(account); + } + connection.disconnect(force); } - updateConversationUi(); - } + } - protected void syncDirtyContacts(Account account) { - for (Contact contact : account.getRoster().getContacts()) { - if (contact.getOption(Contact.Options.DIRTY_PUSH)) { - pushContactToServer(contact); - } - if (contact.getOption(Contact.Options.DIRTY_DELETE)) { - deleteContactOnServer(contact); - } - } - } + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } - public void createContact(Contact contact, boolean autoGrant) { - if (autoGrant) { - contact.setOption(Contact.Options.PREEMPTIVE_GRANT); - contact.setOption(Contact.Options.ASKING); - } - pushContactToServer(contact); - } + public void updateMessage(Message message) { + updateMessage(message, true); + } - public void pushContactToServer(final Contact contact) { - contact.resetOption(Contact.Options.DIRTY_DELETE); - contact.setOption(Contact.Options.DIRTY_PUSH); - 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 IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.query(Namespace.ROSTER).addChild(contact.asElement()); - account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); - if (sendUpdates) { - sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); - } - if (ask) { - sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact)); - } - } else { - syncRoster(contact.getAccount()); - } - } + public void updateMessage(Message message, boolean includeBody) { + databaseBackend.updateMessage(message, includeBody); + updateConversationUi(); + } - 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 updateMessage(Message message, String uuid) { + if (!databaseBackend.updateMessage(message, uuid)) { + Log.e(Config.LOGTAG, "error updated message in DB after edit"); + } + updateConversationUi(); + } - 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(); + protected void syncDirtyContacts(Account account) { + for (Contact contact : account.getRoster().getContacts()) { + if (contact.getOption(Contact.Options.DIRTY_PUSH)) { + pushContactToServer(contact); + } + if (contact.getOption(Contact.Options.DIRTY_DELETE)) { + deleteContactOnServer(contact); + } + } + } - } + public void createContact(Contact contact, boolean autoGrant) { + if (autoGrant) { + contact.setOption(Contact.Options.PREEMPTIVE_GRANT); + contact.setOption(Contact.Options.ASKING); + } + pushContactToServer(contact); + } - private void publishMucAvatar(Conversation conversation, Avatar avatar, OnAvatarPublication callback) { - final IqPacket retrieve = mIqGenerator.retrieveVcardAvatar(avatar); - sendIqPacket(conversation.getAccount(), retrieve, (account, response) -> { - boolean itemNotFound = response.getType() == IqPacket.TYPE.ERROR && response.hasChild("error") && response.findChild("error").hasChild("item-not-found"); - if (response.getType() == IqPacket.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); - IqPacket publication = new IqPacket(IqPacket.TYPE.SET); - publication.setTo(conversation.getJid().asBareJid()); - publication.addChild(vcard); - sendIqPacket(account, publication, (a1, publicationResponse) -> { - if (publicationResponse.getType() == IqPacket.TYPE.RESULT) { - callback.onAvatarPublicationSucceeded(); - } else { - Log.d(Config.LOGTAG, "failed to publish vcard " + publicationResponse.getError()); - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_server_reject); - } - }); - } else { - Log.d(Config.LOGTAG, "failed to request vcard " + response.toString()); - callback.onAvatarPublicationFailed(R.string.error_publish_avatar_no_server_support); - } - }); - } + public void pushContactToServer(final Contact contact) { + contact.resetOption(Contact.Options.DIRTY_DELETE); + contact.setOption(Contact.Options.DIRTY_PUSH); + 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 IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.query(Namespace.ROSTER).addChild(contact.asElement()); + account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); + if (sendUpdates) { + sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); + } + if (ask) { + sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact)); + } + } 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(); + + } + + private void publishMucAvatar(Conversation conversation, Avatar avatar, OnAvatarPublication callback) { + final IqPacket retrieve = mIqGenerator.retrieveVcardAvatar(avatar); + sendIqPacket(conversation.getAccount(), retrieve, (account, response) -> { + boolean itemNotFound = response.getType() == IqPacket.TYPE.ERROR && response.hasChild("error") && response.findChild("error").hasChild("item-not-found"); + if (response.getType() == IqPacket.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); + IqPacket publication = new IqPacket(IqPacket.TYPE.SET); + publication.setTo(conversation.getJid().asBareJid()); + publication.addChild(vcard); + sendIqPacket(account, publication, (a1, publicationResponse) -> { + if (publicationResponse.getType() == IqPacket.TYPE.RESULT) { + callback.onAvatarPublicationSucceeded(); + } else { + Log.d(Config.LOGTAG, "failed to publish vcard " + publicationResponse.getError()); + callback.onAvatarPublicationFailed(R.string.error_publish_avatar_server_reject); + } + }); + } else { + Log.d(Config.LOGTAG, "failed to request vcard " + response.toString()); + callback.onAvatarPublicationFailed(R.string.error_publish_avatar_no_server_support); + } + }); + } public void publishAvatar(Account account, final Avatar avatar, final OnAvatarPublication callback) { final Bundle options; @@ -3406,41 +3414,41 @@ 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); - IqPacket packet = this.mIqGenerator.publishAvatar(avatar, options); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { + 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); + IqPacket packet = this.mIqGenerator.publishAvatar(avatar, options); + this.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket result) { - if (result.getType() == IqPacket.TYPE.RESULT) { - publishAvatarMetadata(account, avatar, options,true, callback); + @Override + public void onIqPacketReceived(Account account, IqPacket result) { + if (result.getType() == IqPacket.TYPE.RESULT) { + publishAvatarMetadata(account, avatar, options, true, callback); } else if (retry && PublishOptions.preconditionNotMet(result)) { - pushNodeConfiguration(account, "urn:xmpp:avatar:data", options, new OnConfigurationPushed() { + pushNodeConfiguration(account, "urn:xmpp:avatar:data", options, new OnConfigurationPushed() { @Override public void onPushSucceeded() { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": changed node configuration for avatar node"); + 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"); + 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) { + public void publishAvatarMetadata(Account account, final Avatar avatar, final Bundle options, final boolean retry, final OnAvatarPublication callback) { final IqPacket packet = XmppConnectionService.this.mIqGenerator.publishAvatarMetadata(avatar, options); sendIqPacket(account, packet, new OnIqPacketReceived() { @Override @@ -3459,14 +3467,14 @@ public class XmppConnectionService extends Service { pushNodeConfiguration(account, "urn:xmpp: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); + 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); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to change node configuration for avatar meta data node"); + publishAvatarMetadata(account, avatar, null, false, callback); } }); } else { @@ -3478,58 +3486,58 @@ public class XmppConnectionService extends Service { }); } - public void republishAvatarIfNeeded(Account account) { - if (account.getAxolotlService().isPepBroken()) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping republication of avatar because pep is broken"); - return; - } - IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { + public void republishAvatarIfNeeded(Account account) { + if (account.getAxolotlService().isPepBroken()) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping republication of avatar because pep is broken"); + return; + } + IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); + this.sendIqPacket(account, packet, new OnIqPacketReceived() { - private Avatar parseAvatar(IqPacket 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; - } + private Avatar parseAvatar(IqPacket 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; + } - private boolean errorIsItemNotFound(IqPacket packet) { - Element error = packet.findChild("error"); - return packet.getType() == IqPacket.TYPE.ERROR - && error != null - && error.hasChild("item-not-found"); - } + private boolean errorIsItemNotFound(IqPacket packet) { + Element error = packet.findChild("error"); + return packet.getType() == IqPacket.TYPE.ERROR + && error != null + && error.hasChild("item-not-found"); + } - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.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 onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.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 fetchAvatar(Account account, Avatar avatar) { - fetchAvatar(account, avatar, null); - } + public void fetchAvatar(Account account, Avatar avatar) { + fetchAvatar(account, avatar, null); + } - public void fetchAvatar(Account account, final Avatar avatar, final UiCallback callback) { - final String KEY = generateFetchKey(account, avatar); - synchronized (this.mInProgressAvatarFetches) { - if (mInProgressAvatarFetches.add(KEY)) { + public void fetchAvatar(Account account, final Avatar avatar, final UiCallback callback) { + final String KEY = generateFetchKey(account, avatar); + synchronized (this.mInProgressAvatarFetches) { + if (mInProgressAvatarFetches.add(KEY)) { switch (avatar.origin) { case PEP: this.mInProgressAvatarFetches.add(KEY); @@ -3541,168 +3549,168 @@ public class XmppConnectionService extends Service { break; } } else if (avatar.origin == Avatar.Origin.PEP) { - mOmittedPepAvatarFetches.add(KEY); + 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(Account account, final Avatar avatar, final UiCallback callback) { - IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar); - sendIqPacket(account, packet, (a, result) -> { - synchronized (mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(a, avatar)); - } - final String ERROR = a.getJid().asBareJid() + ": fetching avatar for " + avatar.owner + " failed "; - if (result.getType() == IqPacket.TYPE.RESULT) { - avatar.image = mIqParser.avatarData(result); - if (avatar.image != null) { - if (getFileBackend().save(avatar)) { - if (a.getJid().asBareJid().equals(avatar.owner)) { - if (a.setAvatar(avatar.getFilename())) { - databaseBackend.updateAccount(a); - } - getAvatarService().clear(a); - updateConversationUi(); - updateAccountUi(); - } else { - Contact contact = a.getRoster().getContact(avatar.owner); - if (contact.setAvatar(avatar)) { - syncRoster(account); - getAvatarService().clear(contact); - updateConversationUi(); - updateRosterUi(); - } - } - if (callback != null) { - callback.success(avatar); - } - Log.d(Config.LOGTAG, a.getJid().asBareJid() - + ": successfully fetched pep avatar for " + avatar.owner); - return; - } - } else { + private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback callback) { + IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar); + sendIqPacket(account, packet, (a, result) -> { + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(a, avatar)); + } + final String ERROR = a.getJid().asBareJid() + ": fetching avatar for " + avatar.owner + " failed "; + if (result.getType() == IqPacket.TYPE.RESULT) { + avatar.image = mIqParser.avatarData(result); + if (avatar.image != null) { + if (getFileBackend().save(avatar)) { + if (a.getJid().asBareJid().equals(avatar.owner)) { + if (a.setAvatar(avatar.getFilename())) { + databaseBackend.updateAccount(a); + } + getAvatarService().clear(a); + updateConversationUi(); + updateAccountUi(); + } else { + Contact contact = a.getRoster().getContact(avatar.owner); + if (contact.setAvatar(avatar)) { + syncRoster(account); + getAvatarService().clear(contact); + updateConversationUi(); + updateRosterUi(); + } + } + if (callback != null) { + callback.success(avatar); + } + Log.d(Config.LOGTAG, a.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); - } + 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); + } - }); - } + }); + } - private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback callback) { - IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - final boolean previouslyOmittedPepFetch; - synchronized (mInProgressAvatarFetches) { - final String KEY = generateFetchKey(account, avatar); - mInProgressAvatarFetches.remove(KEY); - previouslyOmittedPepFetch = mOmittedPepAvatarFetches.remove(KEY); - } - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element vCard = packet.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 { - Contact contact = account.getRoster().getContact(avatar.owner); - if (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()); - if (contact.setAvatar(avatar)) { + private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback callback) { + IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar); + this.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + final boolean previouslyOmittedPepFetch; + synchronized (mInProgressAvatarFetches) { + final String KEY = generateFetchKey(account, avatar); + mInProgressAvatarFetches.remove(KEY); + previouslyOmittedPepFetch = mOmittedPepAvatarFetches.remove(KEY); + } + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element vCard = packet.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 { + Contact contact = account.getRoster().getContact(avatar.owner); + if (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()); + if (contact.setAvatar(avatar)) { syncRoster(account); getAvatarService().clear(contact); updateRosterUi(); } } - } - } - } - } - } - } - } - }); - } + } + } + } + } + } + } + } + }); + } - public void checkForAvatar(Account account, final UiCallback callback) { - IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); - this.sendIqPacket(account, packet, new OnIqPacketReceived() { + public void checkForAvatar(Account account, final UiCallback callback) { + IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); + this.sendIqPacket(account, packet, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element pubsub = packet.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; - } - } - } - } - callback.error(0, null); - } - }); - } + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element pubsub = packet.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; + } + } + } + } + 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"); - for(Conversation conversation : conversations) { + 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"); + for (Conversation conversation : conversations) { if (conversation.getAccount() == account && conversation.getMode() == Conversational.MODE_MULTI) { final MucOptions mucOptions = conversation.getMucOptions(); if (mucOptions.online()) { @@ -3715,871 +3723,871 @@ public class XmppConnectionService extends Service { } } - public void deleteContactOnServer(Contact contact) { - contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); - contact.resetOption(Contact.Options.DIRTY_PUSH); - contact.setOption(Contact.Options.DIRTY_DELETE); - Account account = contact.getAccount(); - if (account.getStatus() == Account.State.ONLINE) { - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - Element item = iq.query(Namespace.ROSTER).addChild("item"); - item.setAttribute("jid", contact.getJid().toString()); - item.setAttribute("subscription", "remove"); - account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); - } - } - - public void updateConversation(final Conversation conversation) { - mDatabaseWriterExecutor.execute(() -> databaseBackend.updateConversation(conversation)); - } - - private void reconnectAccount(final Account account, final boolean force, final boolean interactive) { - synchronized (account) { - XmppConnection connection = account.getXmppConnection(); - if (connection == null) { - connection = createConnection(account); - account.setXmppConnection(connection); - } - boolean hasInternet = hasInternetConnection(); - if (account.isEnabled() && hasInternet) { - if (!force) { - disconnect(account, false); - } - Thread thread = new Thread(connection); - connection.setInteractive(interactive); - connection.prepareNewConnection(); - connection.interrupt(); - thread.start(); - scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode()); - } else { - disconnect(account, force || account.getTrueStatus().isError() || !hasInternet); - account.getRoster().clearPresences(); - connection.resetEverything(); - final AxolotlService axolotlService = account.getAxolotlService(); - if (axolotlService != null) { - axolotlService.resetBrokenness(); - } - if (!hasInternet) { - account.setStatus(Account.State.NO_INTERNET); - } - } - } - } - - public void reconnectAccountInBackground(final Account account) { - new Thread(() -> reconnectAccount(account, false, true)).start(); - } - - public void invite(Conversation conversation, Jid contact) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": inviting " + contact + " to " + conversation.getJid().asBareJid()); - MessagePacket packet = mMessageGenerator.invite(conversation, contact); - sendMessagePacket(conversation.getAccount(), packet); - } - - public void directInvite(Conversation conversation, Jid jid) { - MessagePacket packet = mMessageGenerator.directInvite(conversation, jid); - sendMessagePacket(conversation.getAccount(), packet); - } - - public void resetSendingToWaiting(Account account) { - for (Conversation conversation : getConversations()) { - if (conversation.getAccount() == account) { - conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); - } - } - } - - 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) { - if (uuid == null) { - return null; - } - for (Conversation conversation : getConversations()) { - if (conversation.getJid().asBareJid().equals(recipient) && conversation.getAccount() == account) { - final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid); - if (message != null) { - markMessage(message, status, errorMessage); - } - return message; - } - } - return null; - } - - public boolean markMessage(Conversation conversation, String uuid, int status, String serverMessageId) { - if (uuid == null) { - return false; - } else { - Message message = conversation.findSentMessageWithUuid(uuid); - if (message != null) { - if (message.getServerMsgId() == null) { - message.setServerMsgId(serverMessageId); - } - markMessage(message, status); - return true; - } else { - return false; - } - } - } - - public void markMessage(Message message, int status) { - markMessage(message, status, null); - } - - - public void markMessage(Message message, int status, String errorMessage) { - final int oldStatus = message.getStatus(); - 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) { - return; - } - message.setErrorMessage(errorMessage); - message.setStatus(status); - databaseBackend.updateMessage(message, false); - updateConversationUi(); - } - - private SharedPreferences getPreferences() { - return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - } - - public long getAutomaticMessageDeletionDate() { - final long timeout = getLongPreference(SettingsActivity.AUTOMATIC_MESSAGE_DELETION, R.integer.automatic_message_deletion); - return timeout == 0 ? timeout : (System.currentTimeMillis() - (timeout * 1000)); - } - - public long getLongPreference(String name, @IntegerRes int res) { - long defaultValue = getResources().getInteger(res); - try { - return Long.parseLong(getPreferences().getString(name, String.valueOf(defaultValue))); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public boolean getBooleanPreference(String name, @BoolRes int res) { - return getPreferences().getBoolean(name, getResources().getBoolean(res)); - } - - public boolean confirmMessages() { - return getBooleanPreference("confirm_messages", R.bool.confirm_messages); - } - - public boolean allowMessageCorrection() { - return getBooleanPreference("allow_message_correction", R.bool.allow_message_correction); - } - - public boolean sendChatStates() { - return getBooleanPreference("chat_states", R.bool.chat_states); - } - - private boolean synchronizeWithBookmarks() { - return getBooleanPreference("autojoin", R.bool.autojoin); - } - - public boolean indicateReceived() { - return getBooleanPreference("indicate_received", R.bool.indicate_received); - } - - public boolean useTorToConnect() { - return QuickConversationsService.isConversations() && getBooleanPreference("use_tor", R.bool.use_tor); - } - - public boolean showExtendedConnectionOptions() { - return QuickConversationsService.isConversations() && getBooleanPreference("show_connection_options", R.bool.show_connection_options); - } - - public boolean broadcastLastActivity() { - return getBooleanPreference(SettingsActivity.BROADCAST_LAST_ACTIVITY, R.bool.last_activity); - } - - public int unreadCount() { - int count = 0; - for (Conversation conversation : getConversations()) { - count += conversation.unreadCount(); - } - return count; - } - - - private List threadSafeList(Set set) { - synchronized (LISTENER_LOCK) { - return set.size() == 0 ? Collections.emptyList() : new ArrayList<>(set); - } - } - - public void showErrorToastInUi(int resId) { - for (OnShowErrorToast listener : threadSafeList(this.mOnShowErrorToasts)) { - listener.onShowErrorToast(resId); - } - } - - public void updateConversationUi() { - for (OnConversationUpdate listener : threadSafeList(this.mOnConversationUpdates)) { - listener.onConversationUpdate(); - } - } - - public void updateAccountUi() { - for (OnAccountUpdate listener : threadSafeList(this.mOnAccountUpdates)) { - listener.onAccountUpdate(); - } - } - - public void updateRosterUi() { - for (OnRosterUpdate listener : threadSafeList(this.mOnRosterUpdates)) { - listener.onRosterUpdate(); - } - } - - 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); - for (OnCaptchaRequested listener : threadSafeList(this.mOnCaptchaRequested)) { - listener.onCaptchaRequested(account, id, data, scaled); - } - return true; - } - return false; - } - - public void updateBlocklistUi(final OnUpdateBlocklist.Status status) { - for (OnUpdateBlocklist listener : threadSafeList(this.mOnUpdateBlocklist)) { - listener.OnUpdateBlocklist(status); - } - } - - public void updateMucRosterUi() { - for (OnMucRosterUpdate listener : threadSafeList(this.mOnMucRosterUpdate)) { - listener.onMucRosterUpdate(); - } - } - - public void keyStatusUpdated(AxolotlService.FetchStatus report) { - for (OnKeyStatusUpdated listener : threadSafeList(this.mOnKeyStatusUpdated)) { - listener.onKeyStatusUpdated(report); - } - } - - public Account findAccountByJid(final Jid accountJid) { - for (Account account : this.accounts) { - if (account.getJid().asBareJid().equals(accountJid.asBareJid())) { - return account; - } - } - return null; - } - - public Account findAccountByUuid(final String uuid) { - for(Account account : this.accounts) { - if (account.getUuid().equals(uuid)) { - return account; - } - } - return null; - } - - public Conversation findConversationByUuid(String uuid) { - for (Conversation conversation : getConversations()) { - if (conversation.getUuid().equals(uuid)) { - return conversation; - } - } - return null; - } - - 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))) { - findings.add(c); - } - } - return findings.size() == 1 ? findings.get(0) : null; - } - - public boolean markRead(final Conversation conversation, boolean dismiss) { - return markRead(conversation, null, dismiss).size() > 0; - } - - public void markRead(final Conversation conversation) { - markRead(conversation, null, true); - } - - 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); - } - }; - mDatabaseWriterExecutor.execute(runnable); - updateUnreadCountBadge(); - return readMessages; - } else { - return readMessages; - } - } - - public synchronized void updateUnreadCountBadge() { - int count = unreadCount(); - if (unreadCount != count) { - Log.d(Config.LOGTAG, "update unread count to " + count); - if (count > 0) { - ShortcutBadger.applyCount(getApplicationContext(), count); - } else { - ShortcutBadger.removeCount(getApplicationContext()); - } - unreadCount = count; - } - } - - public void sendReadMarker(final Conversation conversation, String upToUuid) { - final boolean isPrivateAndNonAnonymousMuc = conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous(); - final List readMessages = this.markRead(conversation, upToUuid, true); - if (readMessages.size() > 0) { - updateConversationUi(); - } - final Message markable = Conversation.getLatestMarkableMessage(readMessages, isPrivateAndNonAnonymousMuc); - if (confirmMessages() - && markable != null - && (markable.trusted() || isPrivateAndNonAnonymousMuc) - && markable.getRemoteMsgId() != null) { - Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": sending read marker to " + markable.getCounterpart().toString()); - Account account = conversation.getAccount(); - final Jid to = markable.getCounterpart(); - final boolean groupChat = conversation.getMode() == Conversation.MODE_MULTI; - MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId(), markable.getCounterpart(), groupChat); - this.sendMessagePacket(conversation.getAccount(), packet); - } - } - - public SecureRandom getRNG() { - return this.mRandom; - } - - public MemorizingTrustManager getMemorizingTrustManager() { - return this.mMemorizingTrustManager; - } - - public void setMemorizingTrustManager(MemorizingTrustManager trustManager) { - this.mMemorizingTrustManager = trustManager; - } - - public void updateMemorizingTrustmanager() { - final MemorizingTrustManager tm; - final boolean dontTrustSystemCAs = getBooleanPreference("dont_trust_system_cas", R.bool.dont_trust_system_cas); - if (dontTrustSystemCAs) { - tm = new MemorizingTrustManager(getApplicationContext(), null); - } else { - tm = new MemorizingTrustManager(getApplicationContext()); - } - setMemorizingTrustManager(tm); - } - - public LruCache getBitmapCache() { - return this.mBitmapCache; - } - - public Collection getKnownHosts() { - final Set hosts = new HashSet<>(); - for (final Account account : getAccounts()) { - hosts.add(account.getServer()); - for (final Contact contact : account.getRoster().getContacts()) { - if (contact.showInRoster()) { - final String server = contact.getServer(); - if (server != null) { - hosts.add(server); - } - } - } - } - if (Config.QUICKSY_DOMAIN != null) { - hosts.remove(Config.QUICKSY_DOMAIN); //we only want to show this when we type a e164 number + public void deleteContactOnServer(Contact contact) { + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); + contact.resetOption(Contact.Options.DIRTY_PUSH); + contact.setOption(Contact.Options.DIRTY_DELETE); + Account account = contact.getAccount(); + if (account.getStatus() == Account.State.ONLINE) { + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + Element item = iq.query(Namespace.ROSTER).addChild("item"); + item.setAttribute("jid", contact.getJid().toString()); + item.setAttribute("subscription", "remove"); + account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); } - if (Config.DOMAIN_LOCK != null) { - hosts.add(Config.DOMAIN_LOCK); - } - if (Config.MAGIC_CREATE_DOMAIN != null) { - hosts.add(Config.MAGIC_CREATE_DOMAIN); - } - return hosts; - } + } - public Collection getKnownConferenceHosts() { - final Set mucServers = new HashSet<>(); - for (final Account account : accounts) { - if (account.getXmppConnection() != null) { - mucServers.addAll(account.getXmppConnection().getMucServers()); - for (Bookmark bookmark : account.getBookmarks()) { - final Jid jid = bookmark.getJid(); - final String s = jid == null ? null : jid.getDomain(); - if (s != null) { - mucServers.add(s); - } - } - } - } - return mucServers; - } + public void updateConversation(final Conversation conversation) { + mDatabaseWriterExecutor.execute(() -> databaseBackend.updateConversation(conversation)); + } - public void sendMessagePacket(Account account, MessagePacket packet) { - XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.sendMessagePacket(packet); - } - } - - public void sendPresencePacket(Account account, PresencePacket packet) { - XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.sendPresencePacket(packet); - } - } - - public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) { - final XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data); - connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener, true); - } - } - - public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) { - final XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.sendIqPacket(packet, callback); - } else if (callback != null) { - callback.onIqPacketReceived(account,new IqPacket(IqPacket.TYPE.TIMEOUT)); + private void reconnectAccount(final Account account, final boolean force, final boolean interactive) { + synchronized (account) { + XmppConnection connection = account.getXmppConnection(); + if (connection == null) { + connection = createConnection(account); + account.setXmppConnection(connection); + } + boolean hasInternet = hasInternetConnection(); + if (account.isEnabled() && hasInternet) { + if (!force) { + disconnect(account, false); + } + Thread thread = new Thread(connection); + connection.setInteractive(interactive); + connection.prepareNewConnection(); + connection.interrupt(); + thread.start(); + scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode()); + } else { + disconnect(account, force || account.getTrueStatus().isError() || !hasInternet); + account.getRoster().clearPresences(); + connection.resetEverything(); + final AxolotlService axolotlService = account.getAxolotlService(); + if (axolotlService != null) { + axolotlService.resetBrokenness(); + } + if (!hasInternet) { + account.setStatus(Account.State.NO_INTERNET); + } + } } - } + } - public void sendPresence(final Account account) { - sendPresence(account, checkListeners() && broadcastLastActivity()); - } + public void reconnectAccountInBackground(final Account account) { + new Thread(() -> reconnectAccount(account, false, true)).start(); + } - private void sendPresence(final Account account, final boolean includeIdleTimestamp) { - Presence.Status status; - if (manuallyChangePresence()) { - status = account.getPresenceStatus(); - } else { - status = getTargetPresence(); - } - final PresencePacket 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)); - } - sendPresencePacket(account, packet); - } + public void invite(Conversation conversation, Jid contact) { + Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": inviting " + contact + " to " + conversation.getJid().asBareJid()); + MessagePacket packet = mMessageGenerator.invite(conversation, contact); + sendMessagePacket(conversation.getAccount(), packet); + } - private void deactivateGracePeriod() { - for (Account account : getAccounts()) { - account.deactivateGracePeriod(); - } - } + public void directInvite(Conversation conversation, Jid jid) { + MessagePacket packet = mMessageGenerator.directInvite(conversation, jid); + sendMessagePacket(conversation.getAccount(), packet); + } - public void refreshAllPresences() { - boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity(); - for (Account account : getAccounts()) { - if (account.isEnabled()) { - sendPresence(account, includeIdleTimestamp); - } - } - } + public void resetSendingToWaiting(Account account) { + for (Conversation conversation : getConversations()) { + if (conversation.getAccount() == account) { + conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING)); + } + } + } - private void refreshAllFcmTokens() { - for (Account account : getAccounts()) { - if (account.isOnlineAndConnected() && mPushManagementService.available(account)) { - mPushManagementService.registerPushTokenOnServer(account); - //TODO renew mucs - } - } - } + public Message markMessage(final Account account, final Jid recipient, final String uuid, final int status) { + return markMessage(account, recipient, uuid, status, null); + } - private void sendOfflinePresence(final Account account) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending offline presence"); - sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account)); - } + 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) { + final Message message = conversation.findSentMessageWithUuidOrRemoteId(uuid); + if (message != null) { + markMessage(message, status, errorMessage); + } + return message; + } + } + return null; + } - public MessageGenerator getMessageGenerator() { - return this.mMessageGenerator; - } + public boolean markMessage(Conversation conversation, String uuid, int status, String serverMessageId) { + if (uuid == null) { + return false; + } else { + Message message = conversation.findSentMessageWithUuid(uuid); + if (message != null) { + if (message.getServerMsgId() == null) { + message.setServerMsgId(serverMessageId); + } + markMessage(message, status); + return true; + } else { + return false; + } + } + } - public PresenceGenerator getPresenceGenerator() { - return this.mPresenceGenerator; - } + public void markMessage(Message message, int status) { + markMessage(message, status, null); + } - public IqGenerator getIqGenerator() { - return this.mIqGenerator; - } - public IqParser getIqParser() { - return this.mIqParser; - } + public void markMessage(Message message, int status, String errorMessage) { + final int oldStatus = message.getStatus(); + 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) { + return; + } + message.setErrorMessage(errorMessage); + message.setStatus(status); + databaseBackend.updateMessage(message, false); + updateConversationUi(); + } - public JingleConnectionManager getJingleConnectionManager() { - return this.mJingleConnectionManager; - } + private SharedPreferences getPreferences() { + return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + } - public MessageArchiveService getMessageArchiveService() { - return this.mMessageArchiveService; - } + public long getAutomaticMessageDeletionDate() { + final long timeout = getLongPreference(SettingsActivity.AUTOMATIC_MESSAGE_DELETION, R.integer.automatic_message_deletion); + return timeout == 0 ? timeout : (System.currentTimeMillis() - (timeout * 1000)); + } - public QuickConversationsService getQuickConversationsService() { + public long getLongPreference(String name, @IntegerRes int res) { + long defaultValue = getResources().getInteger(res); + try { + return Long.parseLong(getPreferences().getString(name, String.valueOf(defaultValue))); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public boolean getBooleanPreference(String name, @BoolRes int res) { + return getPreferences().getBoolean(name, getResources().getBoolean(res)); + } + + public boolean confirmMessages() { + return getBooleanPreference("confirm_messages", R.bool.confirm_messages); + } + + public boolean allowMessageCorrection() { + return getBooleanPreference("allow_message_correction", R.bool.allow_message_correction); + } + + public boolean sendChatStates() { + return getBooleanPreference("chat_states", R.bool.chat_states); + } + + private boolean synchronizeWithBookmarks() { + return getBooleanPreference("autojoin", R.bool.autojoin); + } + + public boolean indicateReceived() { + return getBooleanPreference("indicate_received", R.bool.indicate_received); + } + + public boolean useTorToConnect() { + return QuickConversationsService.isConversations() && getBooleanPreference("use_tor", R.bool.use_tor); + } + + public boolean showExtendedConnectionOptions() { + return QuickConversationsService.isConversations() && getBooleanPreference("show_connection_options", R.bool.show_connection_options); + } + + public boolean broadcastLastActivity() { + return getBooleanPreference(SettingsActivity.BROADCAST_LAST_ACTIVITY, R.bool.last_activity); + } + + public int unreadCount() { + int count = 0; + for (Conversation conversation : getConversations()) { + count += conversation.unreadCount(); + } + return count; + } + + + private List threadSafeList(Set set) { + synchronized (LISTENER_LOCK) { + return set.size() == 0 ? Collections.emptyList() : new ArrayList<>(set); + } + } + + public void showErrorToastInUi(int resId) { + for (OnShowErrorToast listener : threadSafeList(this.mOnShowErrorToasts)) { + listener.onShowErrorToast(resId); + } + } + + public void updateConversationUi() { + for (OnConversationUpdate listener : threadSafeList(this.mOnConversationUpdates)) { + listener.onConversationUpdate(); + } + } + + public void updateAccountUi() { + for (OnAccountUpdate listener : threadSafeList(this.mOnAccountUpdates)) { + listener.onAccountUpdate(); + } + } + + public void updateRosterUi() { + for (OnRosterUpdate listener : threadSafeList(this.mOnRosterUpdates)) { + listener.onRosterUpdate(); + } + } + + 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); + for (OnCaptchaRequested listener : threadSafeList(this.mOnCaptchaRequested)) { + listener.onCaptchaRequested(account, id, data, scaled); + } + return true; + } + return false; + } + + public void updateBlocklistUi(final OnUpdateBlocklist.Status status) { + for (OnUpdateBlocklist listener : threadSafeList(this.mOnUpdateBlocklist)) { + listener.OnUpdateBlocklist(status); + } + } + + public void updateMucRosterUi() { + for (OnMucRosterUpdate listener : threadSafeList(this.mOnMucRosterUpdate)) { + listener.onMucRosterUpdate(); + } + } + + public void keyStatusUpdated(AxolotlService.FetchStatus report) { + for (OnKeyStatusUpdated listener : threadSafeList(this.mOnKeyStatusUpdated)) { + listener.onKeyStatusUpdated(report); + } + } + + public Account findAccountByJid(final Jid accountJid) { + for (Account account : this.accounts) { + if (account.getJid().asBareJid().equals(accountJid.asBareJid())) { + return account; + } + } + return null; + } + + public Account findAccountByUuid(final String uuid) { + for (Account account : this.accounts) { + if (account.getUuid().equals(uuid)) { + return account; + } + } + return null; + } + + public Conversation findConversationByUuid(String uuid) { + for (Conversation conversation : getConversations()) { + if (conversation.getUuid().equals(uuid)) { + return conversation; + } + } + return null; + } + + 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))) { + findings.add(c); + } + } + return findings.size() == 1 ? findings.get(0) : null; + } + + public boolean markRead(final Conversation conversation, boolean dismiss) { + return markRead(conversation, null, dismiss).size() > 0; + } + + public void markRead(final Conversation conversation) { + markRead(conversation, null, true); + } + + 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); + } + }; + mDatabaseWriterExecutor.execute(runnable); + updateUnreadCountBadge(); + return readMessages; + } else { + return readMessages; + } + } + + public synchronized void updateUnreadCountBadge() { + int count = unreadCount(); + if (unreadCount != count) { + Log.d(Config.LOGTAG, "update unread count to " + count); + if (count > 0) { + ShortcutBadger.applyCount(getApplicationContext(), count); + } else { + ShortcutBadger.removeCount(getApplicationContext()); + } + unreadCount = count; + } + } + + public void sendReadMarker(final Conversation conversation, String upToUuid) { + final boolean isPrivateAndNonAnonymousMuc = conversation.getMode() == Conversation.MODE_MULTI && conversation.isPrivateAndNonAnonymous(); + final List readMessages = this.markRead(conversation, upToUuid, true); + if (readMessages.size() > 0) { + updateConversationUi(); + } + final Message markable = Conversation.getLatestMarkableMessage(readMessages, isPrivateAndNonAnonymousMuc); + if (confirmMessages() + && markable != null + && (markable.trusted() || isPrivateAndNonAnonymousMuc) + && markable.getRemoteMsgId() != null) { + Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": sending read marker to " + markable.getCounterpart().toString()); + Account account = conversation.getAccount(); + final Jid to = markable.getCounterpart(); + final boolean groupChat = conversation.getMode() == Conversation.MODE_MULTI; + MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId(), markable.getCounterpart(), groupChat); + this.sendMessagePacket(conversation.getAccount(), packet); + } + } + + public SecureRandom getRNG() { + return this.mRandom; + } + + public MemorizingTrustManager getMemorizingTrustManager() { + return this.mMemorizingTrustManager; + } + + public void setMemorizingTrustManager(MemorizingTrustManager trustManager) { + this.mMemorizingTrustManager = trustManager; + } + + public void updateMemorizingTrustmanager() { + final MemorizingTrustManager tm; + final boolean dontTrustSystemCAs = getBooleanPreference("dont_trust_system_cas", R.bool.dont_trust_system_cas); + if (dontTrustSystemCAs) { + tm = new MemorizingTrustManager(getApplicationContext(), null); + } else { + tm = new MemorizingTrustManager(getApplicationContext()); + } + setMemorizingTrustManager(tm); + } + + public LruCache getBitmapCache() { + return this.mBitmapCache; + } + + public Collection getKnownHosts() { + final Set hosts = new HashSet<>(); + for (final Account account : getAccounts()) { + hosts.add(account.getServer()); + for (final Contact contact : account.getRoster().getContacts()) { + if (contact.showInRoster()) { + final String server = contact.getServer(); + if (server != null) { + hosts.add(server); + } + } + } + } + if (Config.QUICKSY_DOMAIN != null) { + hosts.remove(Config.QUICKSY_DOMAIN); //we only want to show this when we type a e164 number + } + if (Config.DOMAIN_LOCK != null) { + hosts.add(Config.DOMAIN_LOCK); + } + if (Config.MAGIC_CREATE_DOMAIN != null) { + hosts.add(Config.MAGIC_CREATE_DOMAIN); + } + return hosts; + } + + public Collection getKnownConferenceHosts() { + final Set mucServers = new HashSet<>(); + for (final Account account : accounts) { + if (account.getXmppConnection() != null) { + mucServers.addAll(account.getXmppConnection().getMucServers()); + for (Bookmark bookmark : account.getBookmarks()) { + final Jid jid = bookmark.getJid(); + final String s = jid == null ? null : jid.getDomain(); + if (s != null) { + mucServers.add(s); + } + } + } + } + return mucServers; + } + + public void sendMessagePacket(Account account, MessagePacket packet) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendMessagePacket(packet); + } + } + + public void sendPresencePacket(Account account, PresencePacket packet) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendPresencePacket(packet); + } + } + + public void sendCreateAccountWithCaptchaPacket(Account account, String id, Data data) { + final XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + IqPacket request = mIqGenerator.generateCreateAccountWithCaptcha(account, id, data); + connection.sendUnmodifiedIqPacket(request, connection.registrationResponseListener, true); + } + } + + public void sendIqPacket(final Account account, final IqPacket packet, final OnIqPacketReceived callback) { + final XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.sendIqPacket(packet, callback); + } else if (callback != null) { + callback.onIqPacketReceived(account, new IqPacket(IqPacket.TYPE.TIMEOUT)); + } + } + + public void sendPresence(final Account account) { + sendPresence(account, checkListeners() && broadcastLastActivity()); + } + + private void sendPresence(final Account account, final boolean includeIdleTimestamp) { + Presence.Status status; + if (manuallyChangePresence()) { + status = account.getPresenceStatus(); + } else { + status = getTargetPresence(); + } + final PresencePacket 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)); + } + sendPresencePacket(account, packet); + } + + private void deactivateGracePeriod() { + for (Account account : getAccounts()) { + account.deactivateGracePeriod(); + } + } + + public void refreshAllPresences() { + boolean includeIdleTimestamp = checkListeners() && broadcastLastActivity(); + for (Account account : getAccounts()) { + if (account.isEnabled()) { + sendPresence(account, includeIdleTimestamp); + } + } + } + + private void refreshAllFcmTokens() { + for (Account account : getAccounts()) { + if (account.isOnlineAndConnected() && mPushManagementService.available(account)) { + mPushManagementService.registerPushTokenOnServer(account); + //TODO renew mucs + } + } + } + + private void sendOfflinePresence(final Account account) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending offline presence"); + sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account)); + } + + public MessageGenerator getMessageGenerator() { + return this.mMessageGenerator; + } + + public PresenceGenerator getPresenceGenerator() { + return this.mPresenceGenerator; + } + + public IqGenerator getIqGenerator() { + return this.mIqGenerator; + } + + public IqParser getIqParser() { + return this.mIqParser; + } + + public JingleConnectionManager getJingleConnectionManager() { + return this.mJingleConnectionManager; + } + + public MessageArchiveService getMessageArchiveService() { + return this.mMessageArchiveService; + } + + public QuickConversationsService getQuickConversationsService() { return this.mQuickConversationsService; } - public List findContacts(Jid jid, String accountJid) { - ArrayList contacts = new ArrayList<>(); - for (Account account : getAccounts()) { - if ((account.isEnabled() || accountJid != null) - && (accountJid == null || accountJid.equals(account.getJid().asBareJid().toString()))) { - Contact contact = account.getRoster().getContactFromContactList(jid); - if (contact != null) { - contacts.add(contact); - } - } - } - return contacts; - } + public List findContacts(Jid jid, String accountJid) { + ArrayList contacts = new ArrayList<>(); + for (Account account : getAccounts()) { + if ((account.isEnabled() || accountJid != null) + && (accountJid == null || accountJid.equals(account.getJid().asBareJid().toString()))) { + Contact contact = account.getRoster().getContactFromContactList(jid); + if (contact != null) { + contacts.add(contact); + } + } + } + return contacts; + } - public Conversation findFirstMuc(Jid jid) { - for (Conversation conversation : getConversations()) { - if (conversation.getAccount().isEnabled() && conversation.getJid().asBareJid().equals(jid.asBareJid()) && conversation.getMode() == Conversation.MODE_MULTI) { - return conversation; - } - } - return null; - } + public Conversation findFirstMuc(Jid jid) { + for (Conversation conversation : getConversations()) { + if (conversation.getAccount().isEnabled() && conversation.getJid().asBareJid().equals(jid.asBareJid()) && conversation.getMode() == Conversation.MODE_MULTI) { + return conversation; + } + } + return null; + } - public NotificationService getNotificationService() { - return this.mNotificationService; - } + public NotificationService getNotificationService() { + return this.mNotificationService; + } - public HttpConnectionManager getHttpConnectionManager() { - return this.mHttpConnectionManager; - } + public HttpConnectionManager getHttpConnectionManager() { + return this.mHttpConnectionManager; + } - public void resendFailedMessages(final Message message) { - final Collection messages = new ArrayList<>(); - Message current = message; - while (current.getStatus() == Message.STATUS_SEND_FAILED) { - messages.add(current); - if (current.mergeable(current.next())) { - current = current.next(); - } else { - break; - } - } - for (final Message msg : messages) { - msg.setTime(System.currentTimeMillis()); - markMessage(msg, Message.STATUS_WAITING); - this.resendMessage(msg, false); - } - if (message.getConversation() instanceof Conversation) { - ((Conversation) message.getConversation()).sort(); - } - updateConversationUi(); - } + public void resendFailedMessages(final Message message) { + final Collection messages = new ArrayList<>(); + Message current = message; + while (current.getStatus() == Message.STATUS_SEND_FAILED) { + messages.add(current); + if (current.mergeable(current.next())) { + current = current.next(); + } else { + break; + } + } + for (final Message msg : messages) { + msg.setTime(System.currentTimeMillis()); + markMessage(msg, Message.STATUS_WAITING); + this.resendMessage(msg, false); + } + if (message.getConversation() instanceof Conversation) { + ((Conversation) message.getConversation()).sort(); + } + updateConversationUi(); + } - public void clearConversationHistory(final Conversation conversation) { - final long clearDate; - final String reference; - if (conversation.countMessages() > 0) { - Message latestMessage = conversation.getLatestMessage(); - clearDate = latestMessage.getTimeSent() + 1000; - reference = latestMessage.getServerMsgId(); - } else { - clearDate = System.currentTimeMillis(); - reference = null; - } - conversation.clearMessages(); - conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam - conversation.setLastClearHistory(clearDate, reference); - Runnable runnable = () -> { - databaseBackend.deleteMessagesInConversation(conversation); - databaseBackend.updateConversation(conversation); - }; - mDatabaseWriterExecutor.execute(runnable); - } + public void clearConversationHistory(final Conversation conversation) { + final long clearDate; + final String reference; + if (conversation.countMessages() > 0) { + Message latestMessage = conversation.getLatestMessage(); + clearDate = latestMessage.getTimeSent() + 1000; + reference = latestMessage.getServerMsgId(); + } else { + clearDate = System.currentTimeMillis(); + reference = null; + } + conversation.clearMessages(); + conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam + conversation.setLastClearHistory(clearDate, reference); + Runnable runnable = () -> { + databaseBackend.deleteMessagesInConversation(conversation); + databaseBackend.updateConversation(conversation); + }; + mDatabaseWriterExecutor.execute(runnable); + } - public boolean sendBlockRequest(final Blockable blockable, boolean reportSpam) { - if (blockable != null && blockable.getBlockedJid() != null) { - final Jid jid = blockable.getBlockedJid(); - this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), (a, response) -> { + public boolean sendBlockRequest(final Blockable blockable, boolean reportSpam) { + if (blockable != null && blockable.getBlockedJid() != null) { + final Jid jid = blockable.getBlockedJid(); + this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), (a, response) -> { if (response.getType() == IqPacket.TYPE.RESULT) { a.getBlocklist().add(jid); updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED); } }); - if (blockable.getBlockedJid().isFullJid()) { - return false; + if (blockable.getBlockedJid().isFullJid()) { + return false; } else if (removeBlockedConversations(blockable.getAccount(), jid)) { - updateConversationUi(); - return true; - } else { - return false; - } - } else { - return false; - } - } + updateConversationUi(); + return true; + } else { + return false; + } + } else { + return false; + } + } - public boolean removeBlockedConversations(final Account account, final Jid blockedJid) { - boolean removed = false; - 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()); - 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"); - updateConversation(conversation); - removed = true; - } - } - } - return removed; - } + public boolean removeBlockedConversations(final Account account, final Jid blockedJid) { + boolean removed = false; + 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()); + 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"); + updateConversation(conversation); + removed = true; + } + } + } + return removed; + } - public void sendUnblockRequest(final Blockable blockable) { - if (blockable != null && blockable.getJid() != null) { - final Jid jid = blockable.getBlockedJid(); - this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(final Account account, final IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - account.getBlocklist().remove(jid); - updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED); - } - } - }); - } - } + public void sendUnblockRequest(final Blockable blockable) { + if (blockable != null && blockable.getJid() != null) { + final Jid jid = blockable.getBlockedJid(); + this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(final Account account, final IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + account.getBlocklist().remove(jid); + updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED); + } + } + }); + } + } - public void publishDisplayName(Account account) { - String displayName = account.getDisplayName(); - final IqPacket request; - if (TextUtils.isEmpty(displayName)) { + public void publishDisplayName(Account account) { + String displayName = account.getDisplayName(); + final IqPacket request; + if (TextUtils.isEmpty(displayName)) { request = mIqGenerator.deleteNode(Namespace.NICK); - } else { + } else { request = mIqGenerator.publishNick(displayName); } mAvatarService.clear(account); sendIqPacket(account, request, (account1, packet) -> { if (packet.getType() == IqPacket.TYPE.ERROR) { - Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": unable to modify nick name "+packet.toString()); + Log.d(Config.LOGTAG, account1.getJid().asBareJid() + ": unable to modify nick name " + packet.toString()); } }); - } + } - public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair key) { - ServiceDiscoveryResult result = discoCache.get(key); - if (result != null) { - return result; - } else { - result = databaseBackend.findDiscoveryResult(key.first, key.second); - if (result != null) { - discoCache.put(key, result); - } - return result; - } - } + public ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair key) { + ServiceDiscoveryResult result = discoCache.get(key); + if (result != null) { + return result; + } else { + result = databaseBackend.findDiscoveryResult(key.first, key.second); + if (result != null) { + discoCache.put(key, result); + } + return result; + } + } - public void fetchCaps(Account account, final Jid jid, final Presence presence) { - final Pair key = new Pair<>(presence.getHash(), presence.getVer()); - ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key); - if (disco != null) { - presence.setServiceDiscoveryResult(disco); - } else { - if (!account.inProgressDiscoFetches.contains(key)) { - account.inProgressDiscoFetches.add(key); - IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.setTo(jid); - final String node = presence.getNode(); - final String ver = presence.getVer(); - final Element query = request.query("http://jabber.org/protocol/disco#info"); - 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, (a, response) -> { - if (response.getType() == IqPacket.TYPE.RESULT) { - ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response); - if (presence.getVer().equals(discoveryResult.getVer())) { - databaseBackend.insertDiscoveryResult(discoveryResult); - injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult); - } else { - Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer()); - } - } - a.inProgressDiscoFetches.remove(key); - }); - } - } - } + public void fetchCaps(Account account, final Jid jid, final Presence presence) { + final Pair key = new Pair<>(presence.getHash(), presence.getVer()); + ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key); + if (disco != null) { + presence.setServiceDiscoveryResult(disco); + } else { + if (!account.inProgressDiscoFetches.contains(key)) { + account.inProgressDiscoFetches.add(key); + IqPacket request = new IqPacket(IqPacket.TYPE.GET); + request.setTo(jid); + final String node = presence.getNode(); + final String ver = presence.getVer(); + final Element query = request.query("http://jabber.org/protocol/disco#info"); + 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, (a, response) -> { + if (response.getType() == IqPacket.TYPE.RESULT) { + ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response); + if (presence.getVer().equals(discoveryResult.getVer())) { + databaseBackend.insertDiscoveryResult(discoveryResult); + injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult); + } else { + Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer()); + } + } + a.inProgressDiscoFetches.remove(key); + }); + } + } + } - private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { - for (Contact contact : roster.getContacts()) { - for (Presence presence : contact.getPresences().getPresences().values()) { - if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) { - presence.setServiceDiscoveryResult(disco); - } - } - } - } + private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { + for (Contact contact : roster.getContacts()) { + for (Presence presence : contact.getPresences().getPresences().values()) { + if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) { + presence.setServiceDiscoveryResult(disco); + } + } + } + } - public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) { - final MessageArchiveService.Version version = MessageArchiveService.Version.get(account); - IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.addChild("prefs", version.namespace); - sendIqPacket(account, request, (account1, packet) -> { - Element prefs = packet.findChild("prefs", version.namespace); - if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) { - callback.onPreferencesFetched(prefs); - } else { - callback.onPreferencesFetchFailed(); - } - }); - } + public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) { + final MessageArchiveService.Version version = MessageArchiveService.Version.get(account); + IqPacket request = new IqPacket(IqPacket.TYPE.GET); + request.addChild("prefs", version.namespace); + sendIqPacket(account, request, (account1, packet) -> { + Element prefs = packet.findChild("prefs", version.namespace); + if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) { + callback.onPreferencesFetched(prefs); + } else { + callback.onPreferencesFetchFailed(); + } + }); + } - public PushManagementService getPushManagementService() { - return mPushManagementService; - } + public PushManagementService getPushManagementService() { + return mPushManagementService; + } - public void changeStatus(Account account, PresenceTemplate template, String signature) { - if (!template.getStatusMessage().isEmpty()) { - databaseBackend.insertPresenceTemplate(template); - } - account.setPgpSignature(signature); - account.setPresenceStatus(template.getStatus()); - account.setPresenceStatusMessage(template.getStatusMessage()); - databaseBackend.updateAccount(account); - sendPresence(account); - } + public void changeStatus(Account account, PresenceTemplate template, String signature) { + if (!template.getStatusMessage().isEmpty()) { + databaseBackend.insertPresenceTemplate(template); + } + account.setPgpSignature(signature); + account.setPresenceStatus(template.getStatus()); + account.setPresenceStatusMessage(template.getStatusMessage()); + databaseBackend.updateAccount(account); + sendPresence(account); + } - public List getPresenceTemplates(Account account) { - List templates = databaseBackend.getPresenceTemplates(); - for (PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) { - if (!templates.contains(template)) { - templates.add(0, template); - } - } - return templates; - } + public List getPresenceTemplates(Account account) { + List templates = databaseBackend.getPresenceTemplates(); + for (PresenceTemplate template : account.getSelfContact().getPresences().asTemplates()) { + if (!templates.contains(template)) { + templates.add(0, template); + } + } + return templates; + } - public void saveConversationAsBookmark(Conversation conversation, String name) { - final Account account = conversation.getAccount(); - final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid()); - final String nick = conversation.getJid().getResource(); + public void saveConversationAsBookmark(Conversation conversation, String name) { + final Account account = conversation.getAccount(); + final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid()); + final String nick = conversation.getJid().getResource(); if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) { bookmark.setNick(nick); } - if (!TextUtils.isEmpty(name)) { - bookmark.setBookmarkName(name); - } - bookmark.setAutojoin(getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin))); - createBookmark(account, bookmark); - bookmark.setConversation(conversation); - } + if (!TextUtils.isEmpty(name)) { + bookmark.setBookmarkName(name); + } + bookmark.setAutojoin(getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin))); + createBookmark(account, bookmark); + bookmark.setConversation(conversation); + } - public boolean verifyFingerprints(Contact contact, List fingerprints) { - boolean performedVerification = false; - final AxolotlService axolotlService = contact.getAccount().getAxolotlService(); - for (XmppUri.Fingerprint fp : fingerprints) { - if (fp.type == XmppUri.FingerprintType.OMEMO) { - String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", ""); - FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint); - if (fingerprintStatus != null) { - if (!fingerprintStatus.isVerified()) { - performedVerification = true; - axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified()); - } - } else { - axolotlService.preVerifyFingerprint(contact, fingerprint); - } - } - } - return performedVerification; - } + public boolean verifyFingerprints(Contact contact, List fingerprints) { + boolean performedVerification = false; + final AxolotlService axolotlService = contact.getAccount().getAxolotlService(); + for (XmppUri.Fingerprint fp : fingerprints) { + if (fp.type == XmppUri.FingerprintType.OMEMO) { + String fingerprint = "05" + fp.fingerprint.replaceAll("\\s", ""); + FingerprintStatus fingerprintStatus = axolotlService.getFingerprintTrust(fingerprint); + if (fingerprintStatus != null) { + if (!fingerprintStatus.isVerified()) { + performedVerification = true; + axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified()); + } + } else { + axolotlService.preVerifyFingerprint(contact, fingerprint); + } + } + } + return performedVerification; + } - public boolean verifyFingerprints(Account account, List fingerprints) { - final AxolotlService axolotlService = account.getAxolotlService(); - boolean verifiedSomething = false; - for (XmppUri.Fingerprint fp : fingerprints) { - 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); - if (fingerprintStatus != null) { - if (!fingerprintStatus.isVerified()) { - axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified()); - verifiedSomething = true; - } - } else { - axolotlService.preVerifyFingerprint(account, fingerprint); - verifiedSomething = true; - } - } - } - return verifiedSomething; - } + public boolean verifyFingerprints(Account account, List fingerprints) { + final AxolotlService axolotlService = account.getAxolotlService(); + boolean verifiedSomething = false; + for (XmppUri.Fingerprint fp : fingerprints) { + 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); + if (fingerprintStatus != null) { + if (!fingerprintStatus.isVerified()) { + axolotlService.setFingerprintTrust(fingerprint, fingerprintStatus.toVerified()); + verifiedSomething = true; + } + } else { + axolotlService.preVerifyFingerprint(account, fingerprint); + verifiedSomething = true; + } + } + } + return verifiedSomething; + } - public boolean blindTrustBeforeVerification() { - return getBooleanPreference(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv); - } + public boolean blindTrustBeforeVerification() { + return getBooleanPreference(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv); + } - public ShortcutService getShortcutService() { - return mShortcutService; - } + public ShortcutService getShortcutService() { + return mShortcutService; + } - public void pushMamPreferences(Account account, Element prefs) { - IqPacket set = new IqPacket(IqPacket.TYPE.SET); - set.addChild(prefs); - sendIqPacket(account, set, null); - } + public void pushMamPreferences(Account account, Element prefs) { + IqPacket set = new IqPacket(IqPacket.TYPE.SET); + set.addChild(prefs); + sendIqPacket(account, set, null); + } - public interface OnMamPreferencesFetched { - void onPreferencesFetched(Element prefs); + public interface OnMamPreferencesFetched { + void onPreferencesFetched(Element prefs); - void onPreferencesFetchFailed(); - } + void onPreferencesFetchFailed(); + } - public interface OnAccountCreated { - void onAccountCreated(Account account); + public interface OnAccountCreated { + void onAccountCreated(Account account); - void informUser(int r); - } + void informUser(int r); + } - public interface OnMoreMessagesLoaded { - void onMoreMessagesLoaded(int count, Conversation conversation); + public interface OnMoreMessagesLoaded { + void onMoreMessagesLoaded(int count, Conversation conversation); - void informUser(int r); - } + void informUser(int r); + } - public interface OnAccountPasswordChanged { - void onPasswordChangeSucceeded(); + public interface OnAccountPasswordChanged { + void onPasswordChangeSucceeded(); - void onPasswordChangeFailed(); - } + void onPasswordChangeFailed(); + } public interface OnRoomDestroy { void onRoomDestroySucceeded(); @@ -4587,63 +4595,63 @@ public class XmppConnectionService extends Service { void onRoomDestroyFailed(); } - public interface OnAffiliationChanged { - void onAffiliationChangedSuccessful(Jid jid); + public interface OnAffiliationChanged { + void onAffiliationChangedSuccessful(Jid jid); - void onAffiliationChangeFailed(Jid jid, int resId); - } + void onAffiliationChangeFailed(Jid jid, int resId); + } - public interface OnConversationUpdate { - void onConversationUpdate(); - } + public interface OnConversationUpdate { + void onConversationUpdate(); + } - public interface OnAccountUpdate { - void onAccountUpdate(); - } + public interface OnAccountUpdate { + void onAccountUpdate(); + } - public interface OnCaptchaRequested { - void onCaptchaRequested(Account account, String id, Data data, Bitmap captcha); - } + public interface OnCaptchaRequested { + void onCaptchaRequested(Account account, String id, Data data, Bitmap captcha); + } - public interface OnRosterUpdate { - void onRosterUpdate(); - } + public interface OnRosterUpdate { + void onRosterUpdate(); + } - public interface OnMucRosterUpdate { - void onMucRosterUpdate(); - } + public interface OnMucRosterUpdate { + void onMucRosterUpdate(); + } - public interface OnConferenceConfigurationFetched { - void onConferenceConfigurationFetched(Conversation conversation); + public interface OnConferenceConfigurationFetched { + void onConferenceConfigurationFetched(Conversation conversation); - void onFetchFailed(Conversation conversation, Element error); - } + void onFetchFailed(Conversation conversation, Element error); + } - public interface OnConferenceJoined { - void onConferenceJoined(Conversation conversation); - } + public interface OnConferenceJoined { + void onConferenceJoined(Conversation conversation); + } - public interface OnConfigurationPushed { - void onPushSucceeded(); + public interface OnConfigurationPushed { + void onPushSucceeded(); - void onPushFailed(); - } + void onPushFailed(); + } - public interface OnShowErrorToast { - void onShowErrorToast(int resId); - } + public interface OnShowErrorToast { + void onShowErrorToast(int resId); + } - public class XmppConnectionBinder extends Binder { - public XmppConnectionService getService() { - return XmppConnectionService.this; - } - } + public class XmppConnectionBinder extends Binder { + public XmppConnectionService getService() { + return XmppConnectionService.this; + } + } - private class InternalEventReceiver extends BroadcastReceiver { + private class InternalEventReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - onStartCommand(intent,0,0); + onStartCommand(intent, 0, 0); } } } diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java index c4eea1d72..b1684473d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -225,13 +225,13 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); Bookmark bookmark = conversation.getBookmark(); if (bookmark != null) { - if (!bookmark.autojoin() && syncAutojoin) { - conversation.getBookmark().setAutojoin(true); + if (!bookmark.autojoin() && syncAutoJoin) { + bookmark.setAutojoin(true); xmppConnectionService.createBookmark(account, bookmark); } } else { bookmark = new Bookmark(account, conversation.getJid().asBareJid()); - bookmark.setAutojoin(syncAutojoin); + bookmark.setAutojoin(syncAutoJoin); xmppConnectionService.createBookmark(account, bookmark); } switchToConversation(conversation); diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 003ea39a2..bc75f4aba 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -1880,7 +1880,7 @@ public class XmppConnection implements Runnable { } public boolean bookmarks2() { - return Config.USE_BOOKMARKS2 || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT); + return Config.USE_BOOKMARKS2 /* || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT)*/; } } }