various mam improvments

iNPUTmice created

Change summary

src/main/java/eu/siacs/conversations/Config.java                         |   4 
src/main/java/eu/siacs/conversations/entities/Conversation.java          |   2 
src/main/java/eu/siacs/conversations/generator/IqGenerator.java          |   7 
src/main/java/eu/siacs/conversations/parser/MessageParser.java           |   6 
src/main/java/eu/siacs/conversations/services/MessageArchiveService.java | 133 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java |  17 
6 files changed, 130 insertions(+), 39 deletions(-)

Detailed changes

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

@@ -22,7 +22,9 @@ 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 static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
+	public static final long MAX_HISTORY_AGE = 7 * MILLISECONDS_IN_DAY;
+	public static final long MAX_CATCHUP =  MILLISECONDS_IN_DAY / 2;
 
 	private Config() {
 

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

@@ -3,6 +3,7 @@ package eu.siacs.conversations.entities;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.os.SystemClock;
+import android.util.Log;
 
 import net.java.otr4j.OtrException;
 import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@@ -20,6 +21,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
 

src/main/java/eu/siacs/conversations/generator/IqGenerator.java 🔗

@@ -1,9 +1,12 @@
 package eu.siacs.conversations.generator;
 
+import android.util.Log;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.services.MessageArchiveService;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.xml.Element;
@@ -103,7 +106,9 @@ public class IqGenerator extends AbstractGenerator {
 		query.setAttribute("queryid",mam.getQueryId());
 		Data data = new Data();
 		data.setFormType("urn:xmpp:mam:0");
-		data.put("with",mam.getWith().toString());
+		if (mam.getWith()!=null) {
+			data.put("with", mam.getWith().toString());
+		}
 		data.put("start",getTimestamp(mam.getStart()));
 		data.put("end",getTimestamp(mam.getEnd()));
 		query.addChild(data);

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

@@ -10,6 +10,7 @@ import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.MessageArchiveService;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.xml.Element;
@@ -303,16 +304,17 @@ public class MessageParser extends AbstractParser implements
 		final long timestamp = getTimestamp(forwarded);
 		final Jid to = message.getAttributeAsJid("to");
 		final Jid from = message.getAttributeAsJid("from");
+		final MessageArchiveService.Query query = this.mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
 		Jid counterpart;
 		int status;
 		Conversation conversation;
 		if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) {
 			status = Message.STATUS_SEND;
-			conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false);
+			conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false,query);
 			counterpart = to;
 		} else if (from !=null && to != null) {
 			status = Message.STATUS_RECEIVED;
-			conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false);
+			conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false,query);
 			counterpart = from;
 		} else {
 			return null;

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

@@ -9,6 +9,8 @@ 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.generator.AbstractGenerator;
+import eu.siacs.conversations.parser.AbstractParser;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@@ -25,26 +27,69 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 		this.mXmppConnectionService = service;
 	}
 
+	public void catchup(final Account account) {
+		long startCatchup = getLastMessageTransmitted(account);
+		long endCatchup = account.getXmppConnection().getLastSessionEstablished();
+		if (startCatchup == 0) {
+			return;
+		} else if (endCatchup - startCatchup >= Config.MAX_CATCHUP) {
+			startCatchup = endCatchup - Config.MAX_CATCHUP;
+			List<Conversation> conversations = mXmppConnectionService.getConversations();
+			for (Conversation conversation : conversations) {
+				if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
+					this.query(conversation,startCatchup);
+				}
+			}
+		}
+		final Query query = new Query(account, startCatchup, endCatchup);
+		this.queries.add(query);
+		this.execute(query);
+	}
+
+	private long getLastMessageTransmitted(final Account account) {
+		long timestamp = 0;
+		for(final Conversation conversation : mXmppConnectionService.getConversations()) {
+			if (conversation.getAccount() == account) {
+				long tmp = conversation.getLastMessageTransmitted();
+				if (tmp > timestamp) {
+					timestamp = tmp;
+				}
+			}
+		}
+		return timestamp;
+	}
+
 	public void query(final Conversation conversation) {
+		query(conversation,conversation.getAccount().getXmppConnection().getLastSessionEstablished());
+	}
+
+	public void query(final Conversation conversation, long end) {
 		synchronized (this.queries) {
 			final Account account = conversation.getAccount();
 			long start = conversation.getLastMessageTransmitted();
-			long end = account.getXmppConnection().getLastSessionEstablished();
-			if (end - start >= Config.MAX_HISTORY_AGE) {
+			if (start > end) {
+				return;
+			} else 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() {
+			this.execute(query);
+		}
+	}
+
+	private void execute(final Query query) {
+		Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid().toString()+": running mam query "+query.toString());
+		IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
+			this.mXmppConnectionService.sendIqPacket(query.getAccount(), packet, new OnIqPacketReceived() {
 				@Override
 				public void onIqPacketReceived(Account account, IqPacket packet) {
 					if (packet.getType() == IqPacket.TYPE_ERROR) {
+						Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": error executing mam: "+packet.toString());
 						finalizeQuery(query);
 					}
 				}
 			});
-		}
 	}
 
 	private void finalizeQuery(Query query) {
@@ -52,11 +97,22 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 			this.queries.remove(query);
 		}
 		final Conversation conversation = query.getConversation();
-		conversation.sort();
-		if (conversation.setLastMessageTransmitted(query.getEnd())) {
-			this.mXmppConnectionService.databaseBackend.updateConversation(conversation);
+		if (conversation != null) {
+			conversation.sort();
+			if (conversation.setLastMessageTransmitted(query.getEnd())) {
+				this.mXmppConnectionService.databaseBackend.updateConversation(conversation);
+			}
+			this.mXmppConnectionService.updateConversationUi();
+		} else {
+			for(Conversation tmp : this.mXmppConnectionService.getConversations()) {
+				if (tmp.getAccount() == query.getAccount()) {
+					tmp.sort();
+					if (tmp.setLastMessageTransmitted(query.getEnd())) {
+						this.mXmppConnectionService.databaseBackend.updateConversation(tmp);
+					}
+				}
+			}
 		}
-		this.mXmppConnectionService.updateConversationUi();
 	}
 
 	public void processFin(Element fin) {
@@ -71,28 +127,18 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 		Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
 		Element last = set == null ? null : set.findChild("last");
 		if (complete || last == null) {
-			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 {
 			final Query nextQuery = query.next(last == null ? null : last.getContent());
-			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(nextQuery);
+			this.execute(nextQuery);
 			synchronized (this.queries) {
 				this.queries.remove(query);
 				this.queries.add(nextQuery);
 			}
-			this.mXmppConnectionService.sendIqPacket(query.getConversation().getAccount(),packet,new OnIqPacketReceived() {
-				@Override
-				public void onIqPacketReceived(Account account, IqPacket packet) {
-					if (packet.getType() == IqPacket.TYPE_ERROR) {
-						finalizeQuery(nextQuery);
-					}
-				}
-			});
 		}
 	}
 
-	private Query findQuery(String id) {
+	public Query findQuery(String id) {
 		if (id == null) {
 			return null;
 		}
@@ -109,36 +155,37 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 	@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");
+			this.catchup(account);
 		}
 	}
 
 	public class Query {
 		private long start;
 		private long end;
-		private Jid with;
+		private Jid with = null;
 		private String queryId;
 		private String after = null;
+		private Account account;
 		private Conversation conversation;
 
 		public Query(Conversation conversation, long start, long end) {
+			this(conversation.getAccount(), start, end);
 			this.conversation = conversation;
 			this.with = conversation.getContactJid().toBareJid();
+		}
+
+		public Query(Account account, long start, long end) {
+			this.account = account;
 			this.start = start;
 			this.end = end;
 			this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
 		}
 
 		public Query next(String after) {
-			Query query = new Query(this.conversation,this.start,this.end);
+			Query query = new Query(this.account,this.start,this.end);
 			query.after = after;
+			query.conversation = conversation;
+			query.with = with;
 			return query;
 		}
 
@@ -165,5 +212,29 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 		public Conversation getConversation() {
 			return conversation;
 		}
+
+		public Account getAccount() {
+			return this.account;
+		}
+
+		@Override
+		public String toString() {
+			StringBuilder builder = new StringBuilder();
+			builder.append("with=");
+			if (this.with==null) {
+				builder.append("*");
+			} else {
+				builder.append(with.toString());
+			}
+			builder.append(", start=");
+			builder.append(AbstractGenerator.getTimestamp(this.start));
+			builder.append(", end=");
+			builder.append(AbstractGenerator.getTimestamp(this.end));
+			if (this.after!=null) {
+				builder.append(", after=");
+				builder.append(this.after);
+			}
+			return builder.toString();
+		}
 	}
 }

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

@@ -988,8 +988,11 @@ public class XmppConnectionService extends Service {
 		return null;
 	}
 
-	public Conversation findOrCreateConversation(final Account account, final Jid jid,
-			final boolean muc) {
+	public Conversation findOrCreateConversation(final Account account, final Jid jid,final boolean muc) {
+		return this.findOrCreateConversation(account,jid,muc,null);
+	}
+
+	public Conversation findOrCreateConversation(final Account account, final Jid jid,final boolean muc, final MessageArchiveService.Query query) {
 		synchronized (this.conversations) {
 			Conversation conversation = find(account, jid);
 			if (conversation != null) {
@@ -1023,7 +1026,13 @@ public class XmppConnectionService extends Service {
 				}
 				this.databaseBackend.createConversation(conversation);
 			}
-			this.mMessageArchiveService.query(conversation);
+			if (query == null) {
+				this.mMessageArchiveService.query(conversation);
+			} else {
+				if (query.getConversation() == null) {
+					this.mMessageArchiveService.query(conversation,query.getStart());
+				}
+			}
 			this.conversations.add(conversation);
 			updateConversationUi();
 			return conversation;
@@ -1303,7 +1312,7 @@ public class XmppConnectionService extends Service {
 
 				@Override
 				public void onFailure() {
-					callback.error(R.string.nick_in_use,conversation);
+					callback.error(R.string.nick_in_use, conversation);
 				}
 			});