MessageArchiveService.java

  1package eu.siacs.conversations.services;
  2
  3import android.util.Log;
  4
  5import java.math.BigInteger;
  6import java.util.HashSet;
  7import java.util.List;
  8
  9import eu.siacs.conversations.Config;
 10import eu.siacs.conversations.entities.Account;
 11import eu.siacs.conversations.entities.Conversation;
 12import eu.siacs.conversations.xml.Element;
 13import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 14import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 15import eu.siacs.conversations.xmpp.jid.Jid;
 16import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 17
 18public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 19
 20	private final XmppConnectionService mXmppConnectionService;
 21
 22	private final HashSet<Query> queries = new HashSet<Query>();
 23
 24	public MessageArchiveService(final XmppConnectionService service) {
 25		this.mXmppConnectionService = service;
 26	}
 27
 28	public void query(final Conversation conversation) {
 29		synchronized (this.queries) {
 30			final Account account = conversation.getAccount();
 31			long start = conversation.getLastMessageReceived();
 32			long end = account.getXmppConnection().getLastSessionEstablished();
 33			if (end - start >= Config.MAX_HISTORY_AGE) {
 34				start = end - Config.MAX_HISTORY_AGE;
 35			}
 36			final Query query = new Query(conversation, start, end);
 37			this.queries.add(query);
 38			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
 39			this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
 40				@Override
 41				public void onIqPacketReceived(Account account, IqPacket packet) {
 42					if (packet.getType() == IqPacket.TYPE_ERROR) {
 43						finalizeQuery(query);
 44					}
 45				}
 46			});
 47		}
 48	}
 49
 50	private void finalizeQuery(Query query) {
 51		synchronized (this.queries) {
 52			this.queries.remove(query);
 53		}
 54		query.getConversation().sort();
 55		this.mXmppConnectionService.updateConversationUi();
 56	}
 57
 58	public void processFin(Element fin) {
 59		if (fin == null) {
 60			return;
 61		}
 62		Query query = findQuery(fin.getAttribute("queryid"));
 63		if (query == null) {
 64			return;
 65		}
 66		boolean complete = fin.getAttributeAsBoolean("complete");
 67		Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
 68		Element last = set == null ? null : set.findChild("last");
 69		if (complete || last == null) {
 70			final Account account = query.getConversation().getAccount();
 71			Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": completed mam query for "+query.getWith().toString());
 72			this.finalizeQuery(query);
 73		} else {
 74			final Query nextQuery = query.next(last == null ? null : last.getContent());
 75			IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(nextQuery);
 76			synchronized (this.queries) {
 77				this.queries.remove(query);
 78				this.queries.add(nextQuery);
 79			}
 80			this.mXmppConnectionService.sendIqPacket(query.getConversation().getAccount(),packet,new OnIqPacketReceived() {
 81				@Override
 82				public void onIqPacketReceived(Account account, IqPacket packet) {
 83					if (packet.getType() == IqPacket.TYPE_ERROR) {
 84						finalizeQuery(nextQuery);
 85					}
 86				}
 87			});
 88		}
 89	}
 90
 91	private Query findQuery(String id) {
 92		if (id == null) {
 93			return null;
 94		}
 95		synchronized (this.queries) {
 96			for(Query query : this.queries) {
 97				if (query.getQueryId().equals(id)) {
 98					return query;
 99				}
100			}
101			return null;
102		}
103	}
104
105	@Override
106	public void onAdvancedStreamFeaturesAvailable(Account account) {
107		if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
108			List<Conversation> conversations = mXmppConnectionService.getConversations();
109			for (Conversation conversation : conversations) {
110				if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account) {
111					this.query(conversation);
112				}
113			}
114		} else {
115			Log.d(Config.LOGTAG,"no mam available");
116		}
117	}
118
119	public class Query {
120		private long start;
121		private long end;
122		private Jid with;
123		private String queryId;
124		private String after = null;
125		private Conversation conversation;
126
127		public Query(Conversation conversation, long start, long end) {
128			this.conversation = conversation;
129			this.with = conversation.getContactJid().toBareJid();
130			this.start = start;
131			this.end = end;
132			this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
133		}
134
135		public Query next(String after) {
136			Query query = new Query(this.conversation,this.start,this.end);
137			query.after = after;
138			return query;
139		}
140
141		public String getAfter() {
142			return after;
143		}
144
145		public String getQueryId() {
146			return queryId;
147		}
148
149		public Jid getWith() {
150			return with;
151		}
152
153		public long getStart() {
154			return start;
155		}
156
157		public long getEnd() {
158			return end;
159		}
160
161		public Conversation getConversation() {
162			return conversation;
163		}
164	}
165}