set autojoin=true after following invite

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 923 
src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java    |   6 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java            |   2 
3 files changed, 465 insertions(+), 466 deletions(-)

Detailed changes

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<DatabaseBackend.FilePathInfo> relativeFilePaths = databaseBackend.getFilePathInfo();
         final List<DatabaseBackend.FilePathInfo> 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<Jid,Bookmark> bookmarks, final boolean pep) {
+    public void processBookmarksInitial(Account account, Map<Jid, Bookmark> bookmarks, final boolean pep) {
         final Set<Jid> 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<String, Account> 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<Conversation> 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));
-	}
-
-	public void loadPhoneContacts() {
+    private void restoreFromDatabase() {
+        synchronized (this.conversations) {
+            final Map<String, Account> 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<Conversation> 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));
+    }
+
+    public void loadPhoneContacts() {
         mContactMergerExecutor.execute(() -> {
             Map<Jid, JabberIdContact> 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<Conversation> getConversations() {
-		return this.conversations;
-	}
+    public List<Conversation> 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<String> 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<String> uuids) {
+    private void markUuidsAsDeletedFiles(List<String> 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<Conversation> list) {
-		populateWithOrderedConversations(list, true, true);
-	}
+    public void populateWithOrderedConversations(final List<Conversation> list) {
+        populateWithOrderedConversations(list, true, true);
+    }
 
-	public void populateWithOrderedConversations(final List<Conversation> list, final boolean includeNoFileUpload) {
+    public void populateWithOrderedConversations(final List<Conversation> list, final boolean includeNoFileUpload) {
         populateWithOrderedConversations(list, includeNoFileUpload, true);
     }
 
-	public void populateWithOrderedConversations(final List<Conversation> list, final boolean includeNoFileUpload, final boolean sort) {
+    public void populateWithOrderedConversations(final List<Conversation> list, final boolean includeNoFileUpload, final boolean sort) {
         final List<String> 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
-		}
-	}
-
-	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<Message> 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);
-	}
-
-	public List<Account> getAccounts() {
-		return this.accounts;
-	}
+        } 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<Message> 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);
+    }
+
+    public List<Account> 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<Conversation> findAllConferencesWith(Contact contact) {
-		ArrayList<Conversation> 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<Conversation> haystack, final Contact contact) {
-		for (final Conversation conversation : haystack) {
-			if (conversation.getContact() == contact) {
-				return conversation;
-			}
-		}
-		return null;
-	}
-
-	public Conversation find(final Iterable<Conversation> 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);
-						}
-					}
-				}
+    public List<Conversation> findAllConferencesWith(Contact contact) {
+        ArrayList<Conversation> 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<Conversation> haystack, final Contact contact) {
+        for (final Conversation conversation : haystack) {
+            if (conversation.getContact() == contact) {
+                return conversation;
+            }
+        }
+        return null;
+    }
+
+    public Conversation find(final Iterable<Conversation> 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();
-		}
-	}
-
-	public void stopPresenceUpdatesTo(Contact contact) {
+                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) {
         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();
-	}
-
-	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;
+    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 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");
-        }
-    }
-
-	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<Jid, String> 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<Jid, String> 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 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) {
+            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<Jid, String> 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<Jid, String> 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 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) {
                 account.getAxolotlService().deleteOmemoIdentity();
             }
             for (final Conversation conversation : conversations) {

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

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