added max history age (default 1w). automatically sort newly added mam messages

iNPUTmice created

Change summary

src/main/java/eu/siacs/conversations/Config.java                              |  2 
src/main/java/eu/siacs/conversations/entities/AbstractEntity.java             |  1 
src/main/java/eu/siacs/conversations/entities/Conversation.java               | 39 
src/main/java/eu/siacs/conversations/entities/Message.java                    |  5 
src/main/java/eu/siacs/conversations/parser/MessageParser.java                |  7 
src/main/java/eu/siacs/conversations/services/MessageArchiveService.java      | 48 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java      | 25 
src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java |  2 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java                 | 16 
9 files changed, 103 insertions(+), 42 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/Config.java 🔗

@@ -22,6 +22,8 @@ public final class Config {
 
 	public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
 
+	public static final long MAX_HISTORY_AGE = 7 * 24 * 60 * 60 * 1000;
+
 	private Config() {
 
 	}

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

@@ -16,6 +16,8 @@ import org.json.JSONObject;
 
 import java.security.interfaces.DSAPublicKey;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -471,12 +473,25 @@ public class Conversation extends AbstractEntity {
 		}
 	}
 
-	public void setLastMessageReceived(long value) {
+	public boolean setLastMessageReceived(long value) {
+		long before = getLastMessageReceived();
 		this.setAttribute(ATTRIBUTE_LAST_MESSAGE_RECEIVED, String.valueOf(value));
+		return (value - before > 1000);
 	}
 
 	public long getLastMessageReceived() {
-		return getLongAttribute(ATTRIBUTE_LAST_MESSAGE_RECEIVED,0);
+		long timestamp = getLongAttribute(ATTRIBUTE_LAST_MESSAGE_RECEIVED,0);
+		if (timestamp == 0) {
+			synchronized (this.messages) {
+				for(int i = this.messages.size() - 1; i >= 0; --i) {
+					Message message = this.messages.get(i);
+					if (message.getStatus() == Message.STATUS_RECEIVED) {
+						return message.getTimeSent();
+					}
+				}
+			}
+		}
+		return timestamp;
 	}
 
 	public void setMutedTill(long value) {
@@ -544,6 +559,26 @@ public class Conversation extends AbstractEntity {
 		}
 	}
 
+	public void sort() {
+		synchronized (this.messages) {
+			for(Message message : this.messages) {
+				message.untie();
+			}
+			Collections.sort(this.messages,new Comparator<Message>() {
+				@Override
+				public int compare(Message left, Message right) {
+					if (left.getTimeSent() < right.getTimeSent()) {
+						return -1;
+					} else if (left.getTimeSent() > right.getTimeSent()) {
+						return 1;
+					} else {
+						return 0;
+					}
+				}
+			});
+		}
+	}
+
 	public class Smp {
 		public static final int STATUS_NONE = 0;
 		public static final int STATUS_CONTACT_REQUESTED = 1;

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

@@ -549,8 +549,11 @@ public class MessageParser extends AbstractParser implements
 		}
 		Conversation conversation = message.getConversation();
 		conversation.add(message);
-		conversation.setLastMessageReceived(System.currentTimeMillis());
-		mXmppConnectionService.updateConversation(conversation);
+		if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) {
+			if (conversation.setLastMessageReceived(System.currentTimeMillis())) {
+				mXmppConnectionService.updateConversation(conversation);
+			}
+		}
 
 		if (message.getStatus() == Message.STATUS_RECEIVED
 				&& conversation.getOtrSession() != null

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

@@ -4,16 +4,18 @@ import android.util.Log;
 
 import java.math.BigInteger;
 import java.util.HashSet;
+import java.util.List;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 import eu.siacs.conversations.xmpp.jid.Jid;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 
-public class MessageArchiveService {
+public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 
 	private final XmppConnectionService mXmppConnectionService;
 
@@ -28,18 +30,31 @@ public class MessageArchiveService {
 			final Account account = conversation.getAccount();
 			long start = conversation.getLastMessageReceived();
 			long end = account.getXmppConnection().getLastSessionEstablished();
+			if (end - start >= Config.MAX_HISTORY_AGE) {
+				start = end - Config.MAX_HISTORY_AGE;
+			}
 			final Query query = new Query(conversation, start, end);
 			this.queries.add(query);
 			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
 			this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
 				@Override
 				public void onIqPacketReceived(Account account, IqPacket packet) {
-					Log.d(Config.LOGTAG, packet.toString());
+					if (packet.getType() == IqPacket.TYPE_ERROR) {
+						finalizeQuery(query);
+					}
 				}
 			});
 		}
 	}
 
+	private void finalizeQuery(Query query) {
+		synchronized (this.queries) {
+			this.queries.remove(query);
+		}
+		query.getConversation().sort();
+		this.mXmppConnectionService.updateConversationUi();
+	}
+
 	public void processFin(Element fin) {
 		if (fin == null) {
 			return;
@@ -48,27 +63,26 @@ public class MessageArchiveService {
 		if (query == null) {
 			return;
 		}
-		Log.d(Config.LOGTAG,"fin "+fin.toString());
 		boolean complete = fin.getAttributeAsBoolean("complete");
 		Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
 		Element last = set == null ? null : set.findChild("last");
 		if (complete || last == null) {
-			Log.d(Config.LOGTAG,"completed mam query for "+query.getWith().toString());
-			synchronized (this.queries) {
-				this.queries.remove(query);
-			}
+			final Account account = query.getConversation().getAccount();
+			Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": completed mam query for "+query.getWith().toString());
+			this.finalizeQuery(query);
 		} else {
-			Query nextQuery = query.next(last == null ? null : last.getContent());
+			final Query nextQuery = query.next(last == null ? null : last.getContent());
 			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(nextQuery);
 			synchronized (this.queries) {
 				this.queries.remove(query);
 				this.queries.add(nextQuery);
 			}
-			Log.d(Config.LOGTAG,packet.toString());
 			this.mXmppConnectionService.sendIqPacket(query.getConversation().getAccount(),packet,new OnIqPacketReceived() {
 				@Override
 				public void onIqPacketReceived(Account account, IqPacket packet) {
-					Log.d(Config.LOGTAG,packet.toString());
+					if (packet.getType() == IqPacket.TYPE_ERROR) {
+						finalizeQuery(nextQuery);
+					}
 				}
 			});
 		}
@@ -88,6 +102,20 @@ public class MessageArchiveService {
 		}
 	}
 
+	@Override
+	public void onAdvancedStreamFeaturesAvailable(Account account) {
+		if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+			List<Conversation> conversations = mXmppConnectionService.getConversations();
+			for (Conversation conversation : conversations) {
+				if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account) {
+					this.query(conversation);
+				}
+			}
+		} else {
+			Log.d(Config.LOGTAG,"no mam available");
+		}
+	}
+
 	public class Query {
 		private long start;
 		private long end;

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

@@ -73,7 +73,7 @@ import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
 import eu.siacs.conversations.utils.PRNGFixes;
 import eu.siacs.conversations.utils.PhoneHelper;
 import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesAvailable;
+import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 import eu.siacs.conversations.xmpp.OnBindListener;
 import eu.siacs.conversations.xmpp.OnContactStatusChanged;
 import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@@ -205,12 +205,7 @@ public class XmppConnectionService extends Service {
 			getNotificationService().updateErrorNotification();
 		}
 	};
-	private OnAdvancedStreamFeaturesAvailable onAdvancedStreamFeaturesAvailable = new OnAdvancedStreamFeaturesAvailable() {
-		@Override
-		public void onAdvancedStreamFeaturesAvailable(Account account) {
-			queryMessagesFromArchive(account);
-		}
-	};
+
 	private int accountChangedListenerCount = 0;
 	private OnRosterUpdate mOnRosterUpdate = null;
 	private int rosterChangedListenerCount = 0;
@@ -592,7 +587,7 @@ public class XmppConnectionService extends Service {
 		connection.setOnJinglePacketReceivedListener(this.jingleListener);
 		connection.setOnBindListener(this.mOnBindListener);
 		connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
-		connection.setOnAdvancedStreamFeaturesAvailableListener(this.onAdvancedStreamFeaturesAvailable);
+		connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
 		return connection;
 	}
 
@@ -1027,6 +1022,7 @@ public class XmppConnectionService extends Service {
 				}
 				this.databaseBackend.createConversation(conversation);
 			}
+			this.mMessageArchiveService.query(conversation);
 			this.conversations.add(conversation);
 			updateConversationUi();
 			return conversation;
@@ -1239,19 +1235,6 @@ public class XmppConnectionService extends Service {
 		}
 	}
 
-	private void queryMessagesFromArchive(final Account account) {
-		if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
-			List<Conversation> conversations = getConversations();
-			for (Conversation conversation : conversations) {
-				if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account) {
-					this.mMessageArchiveService.query(conversation);
-				}
-			}
-		} else {
-			Log.d(Config.LOGTAG,"no mam available");
-		}
-	}
-
 	public void joinMuc(Conversation conversation) {
 		Account account = conversation.getAccount();
 		account.pendingConferenceJoins.remove(conversation);

src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesAvailable.java → src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java 🔗

@@ -2,6 +2,6 @@ package eu.siacs.conversations.xmpp;
 
 import eu.siacs.conversations.entities.Account;
 
-public interface OnAdvancedStreamFeaturesAvailable {
+public interface OnAdvancedStreamFeaturesLoaded {
 	public void onAdvancedStreamFeaturesAvailable(final Account account);
 }

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -107,7 +107,7 @@ public class XmppConnection implements Runnable {
 	private OnMessagePacketReceived messageListener = null;
 	private OnStatusChanged statusListener = null;
 	private OnBindListener bindListener = null;
-	private OnAdvancedStreamFeaturesAvailable advancedStreamFeaturesAvailableListener = null;
+	private ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
 	private OnMessageAcknowledged acknowledgedListener = null;
 	private XmppConnectionService mXmppConnectionService = null;
 
@@ -772,8 +772,8 @@ public class XmppConnection implements Runnable {
 
 					if (account.getServer().equals(server.toDomainJid())) {
 						enableAdvancedStreamFeatures();
-						if (advancedStreamFeaturesAvailableListener != null) {
-							advancedStreamFeaturesAvailableListener.onAdvancedStreamFeaturesAvailable(account);
+						for(OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
+							listener.onAdvancedStreamFeaturesAvailable(account);
 						}
 					}
 				}
@@ -947,8 +947,10 @@ public class XmppConnection implements Runnable {
 		this.acknowledgedListener = listener;
 	}
 
-	public void setOnAdvancedStreamFeaturesAvailableListener(OnAdvancedStreamFeaturesAvailable listener) {
-		this.advancedStreamFeaturesAvailableListener = listener;
+	public void addOnAdvancedStreamFeaturesAvailableListener(OnAdvancedStreamFeaturesLoaded listener) {
+		if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
+			this.advancedStreamFeaturesLoadedListeners.add(listener);
+		}
 	}
 
 	public void disconnect(boolean force) {
@@ -1095,6 +1097,10 @@ public class XmppConnection implements Runnable {
 			return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
 		}
 
+		public boolean advancedStreamFeaturesLoaded() {
+			return disco.containsKey(account.getServer().toString());
+		}
+
 		public boolean rosterVersioning() {
 			return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
 		}