go through mam history page by page. load mam dynamically on scroll

This commit is contained in:
iNPUTmice 2014-12-15 23:06:29 +01:00
parent 03ca971e2e
commit e2f50ab855
5 changed files with 108 additions and 43 deletions

View file

@ -18,13 +18,15 @@ public final class Config {
public static final int MESSAGE_MERGE_WINDOW = 20; public static final int MESSAGE_MERGE_WINDOW = 20;
public static final int PAGE_SIZE = 50;
public static final int PROGRESS_UI_UPDATE_INTERVAL = 750; public static final int PROGRESS_UI_UPDATE_INTERVAL = 750;
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
private static final long MILLISECONDS_IN_DAY = 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 MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
public static final long MAX_CATCHUP = MILLISECONDS_IN_DAY / 2; public static final int MAM_MAX_MESSAGES = 500;
private Config() { private Config() {

View file

@ -324,11 +324,8 @@ public class MessageParser extends AbstractParser implements
finishedMessage.setCounterpart(counterpart); finishedMessage.setCounterpart(counterpart);
finishedMessage.setRemoteMsgId(message.getAttribute("id")); finishedMessage.setRemoteMsgId(message.getAttribute("id"));
finishedMessage.setServerMsgId(result.getAttribute("id")); finishedMessage.setServerMsgId(result.getAttribute("id"));
if (conversation.hasDuplicateMessage(finishedMessage)) { if (query!=null) {
Log.d(Config.LOGTAG, "received mam message " + content+ " (duplicate)"); query.incrementCount();
return null;
} else {
Log.d(Config.LOGTAG, "received mam message " + content);
} }
return finishedMessage; return finishedMessage;
} }

View file

@ -12,7 +12,6 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.generator.AbstractGenerator; import eu.siacs.conversations.generator.AbstractGenerator;
import eu.siacs.conversations.parser.AbstractParser;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
@ -40,8 +39,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
long endCatchup = account.getXmppConnection().getLastSessionEstablished(); long endCatchup = account.getXmppConnection().getLastSessionEstablished();
if (startCatchup == 0) { if (startCatchup == 0) {
return; return;
} else if (endCatchup - startCatchup >= Config.MAX_CATCHUP) { } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
startCatchup = endCatchup - Config.MAX_CATCHUP; startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
List<Conversation> conversations = mXmppConnectionService.getConversations(); List<Conversation> conversations = mXmppConnectionService.getConversations();
for (Conversation conversation : conversations) { for (Conversation conversation : conversations) {
if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) { if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
@ -67,22 +66,23 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return timestamp; return timestamp;
} }
public void query(final Conversation conversation) { public Query query(final Conversation conversation) {
query(conversation,conversation.getAccount().getXmppConnection().getLastSessionEstablished()); return query(conversation,conversation.getAccount().getXmppConnection().getLastSessionEstablished());
} }
public void query(final Conversation conversation, long end) { public Query query(final Conversation conversation, long end) {
return this.query(conversation,conversation.getLastMessageTransmitted(),end);
}
public Query query(Conversation conversation, long start, long end) {
synchronized (this.queries) { synchronized (this.queries) {
final Account account = conversation.getAccount();
long start = conversation.getLastMessageTransmitted();
if (start > end) { if (start > end) {
return; return null;
} else if (end - start >= Config.MAX_HISTORY_AGE) {
start = end - Config.MAX_HISTORY_AGE;
} }
final Query query = new Query(conversation, start, end,PagingOrder.REVERSE); final Query query = new Query(conversation, start, end,PagingOrder.REVERSE);
this.queries.add(query); this.queries.add(query);
this.execute(query); this.execute(query);
return query;
} }
} }
@ -133,7 +133,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
if (conversation.setLastMessageTransmitted(query.getEnd())) { if (conversation.setLastMessageTransmitted(query.getEnd())) {
this.mXmppConnectionService.databaseBackend.updateConversation(conversation); this.mXmppConnectionService.databaseBackend.updateConversation(conversation);
} }
if (query.hasCallback()) {
query.callback();
} else {
this.mXmppConnectionService.updateConversationUi(); this.mXmppConnectionService.updateConversationUi();
}
} else { } else {
for(Conversation tmp : this.mXmppConnectionService.getConversations()) { for(Conversation tmp : this.mXmppConnectionService.getConversations()) {
if (tmp.getAccount() == query.getAccount()) { if (tmp.getAccount() == query.getAccount()) {
@ -170,8 +174,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
Element last = set == null ? null : set.findChild("last"); Element last = set == null ? null : set.findChild("last");
Element first = set == null ? null : set.findChild("first"); Element first = set == null ? null : set.findChild("first");
Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first; Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
if (complete || relevant == null) { boolean abort = (query.getStart() == 0 && query.getTotalCount() >= Config.PAGE_SIZE) || query.getTotalCount() >= Config.MAM_MAX_MESSAGES;
if (complete || relevant == null || abort) {
this.finalizeQuery(query); this.finalizeQuery(query);
Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid().toString()+": finished mam after "+query.getTotalCount()+" messages");
} else { } else {
final Query nextQuery; final Query nextQuery;
if (query.getPagingOrder() == PagingOrder.NORMAL) { if (query.getPagingOrder() == PagingOrder.NORMAL) {
@ -210,6 +216,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
} }
public class Query { public class Query {
private int totalCount = 0;
private int count = 0;
private long start; private long start;
private long end; private long end;
private Jid with = null; private Jid with = null;
@ -218,6 +226,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private Account account; private Account account;
private Conversation conversation; private Conversation conversation;
private PagingOrder pagingOrder = PagingOrder.NORMAL; private PagingOrder pagingOrder = PagingOrder.NORMAL;
private XmppConnectionService.OnMoreMessagesLoaded callback = null;
public Query(Conversation conversation, long start, long end) { public Query(Conversation conversation, long start, long end) {
@ -243,6 +252,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
query.reference = reference; query.reference = reference;
query.conversation = conversation; query.conversation = conversation;
query.with = with; query.with = with;
query.totalCount = totalCount;
query.callback = callback;
return query; return query;
} }
@ -278,6 +289,16 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return start; return start;
} }
public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
this.callback = callback;
}
public void callback() {
if (this.callback != null) {
this.callback.onMoreMessagesLoaded(count,conversation);
}
}
public long getEnd() { public long getEnd() {
return end; return end;
} }
@ -290,6 +311,15 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return this.account; return this.account;
} }
public void incrementCount() {
this.count++;
this.totalCount++;
}
public int getTotalCount() {
return this.totalCount;
}
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -313,5 +343,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
} }
return builder.toString(); return builder.toString();
} }
public boolean hasCallback() {
return this.callback != null;
}
} }
} }

View file

@ -246,8 +246,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
account.pendingConferenceLeaves.clear(); account.pendingConferenceLeaves.clear();
fetchRosterFromServer(account); fetchRosterFromServer(account);
fetchBookmarks(account); fetchBookmarks(account);
sendPresencePacket(account, sendPresencePacket(account,mPresenceGenerator.sendPresence(account));
mPresenceGenerator.sendPresence(account));
connectMultiModeConversations(account); connectMultiModeConversations(account);
updateConversationUi(); updateConversationUi();
} }
@ -893,11 +892,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
accountLookupTable.put(account.getUuid(), account); accountLookupTable.put(account.getUuid(), account);
} }
this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE)); this.conversations.addAll(databaseBackend.getConversations(Conversation.STATUS_AVAILABLE));
for (Conversation conv : this.conversations) { for (Conversation conversation : this.conversations) {
Account account = accountLookupTable.get(conv.getAccountUuid()); Account account = accountLookupTable.get(conversation.getAccountUuid());
conv.setAccount(account); conversation.setAccount(account);
conv.addAll(0, databaseBackend.getMessages(conv, 50)); conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
checkDeletedFiles(conv); checkDeletedFiles(conversation);
} }
} }
} }
@ -962,17 +961,29 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}); });
} }
public int loadMoreMessages(Conversation conversation, long timestamp) { public void loadMoreMessages(Conversation conversation, long timestamp, final OnMoreMessagesLoaded callback) {
if (this.getMessageArchiveService().queryInProgress(conversation)) { if (this.getMessageArchiveService().queryInProgress(conversation)) {
return 0; Log.d(Config.LOGTAG,"query in progress");
return;
}
List<Message> messages = databaseBackend.getMessages(conversation, 50,timestamp);
if (messages.size() == 0 && (conversation.getAccount().getXmppConnection() != null && conversation.getAccount().getXmppConnection().getFeatures().mam())) {
Log.d(Config.LOGTAG,"load more messages with mam");
MessageArchiveService.Query query = getMessageArchiveService().query(conversation,0,timestamp - 1);
if (query != null) {
query.setCallback(callback);
}
return;
} }
List<Message> messages = databaseBackend.getMessages(conversation, 50,
timestamp);
for (Message message : messages) { for (Message message : messages) {
message.setConversation(conversation); message.setConversation(conversation);
} }
conversation.addAll(0, messages); conversation.addAll(0, messages);
return messages.size(); callback.onMoreMessagesLoaded(messages.size(),conversation);
}
public interface OnMoreMessagesLoaded {
public void onMoreMessagesLoaded(int count,Conversation conversation);
} }
public List<Account> getAccounts() { public List<Account> getAccounts() {
@ -1022,7 +1033,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else { } else {
conversation.setMode(Conversation.MODE_SINGLE); conversation.setMode(Conversation.MODE_SINGLE);
} }
conversation.addAll(0, databaseBackend.getMessages(conversation, 50)); conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
this.databaseBackend.updateConversation(conversation); this.databaseBackend.updateConversation(conversation);
} else { } else {
String conversationName; String conversationName;
@ -1244,11 +1255,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
Log.d(Config.LOGTAG, "app switched into background"); Log.d(Config.LOGTAG, "app switched into background");
} }
public void connectMultiModeConversations(Account account) { private void connectMultiModeConversations(Account account) {
List<Conversation> conversations = getConversations(); List<Conversation> conversations = getConversations();
for (Conversation conversation : conversations) { for (Conversation conversation : conversations) {
if ((conversation.getMode() == Conversation.MODE_MULTI) if ((conversation.getMode() == Conversation.MODE_MULTI)
&& (conversation.getAccount() == account)) { && (conversation.getAccount() == account)) {
conversation.resetMucOptions();
joinMuc(conversation); joinMuc(conversation);
} }
} }

View file

@ -9,6 +9,7 @@ import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity; import android.view.Gravity;
@ -38,6 +39,7 @@ import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -104,6 +106,7 @@ public class ConversationFragment extends Fragment {
private TextView snackbarMessage; private TextView snackbarMessage;
private TextView snackbarAction; private TextView snackbarAction;
private boolean messagesLoaded = false; private boolean messagesLoaded = false;
private OnScrollListener mOnScrollListener = new OnScrollListener() { private OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override @Override
@ -119,14 +122,30 @@ public class ConversationFragment extends Fragment {
if (firstVisibleItem == 0 && messagesLoaded) { if (firstVisibleItem == 0 && messagesLoaded) {
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent(); long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
messagesLoaded = false; messagesLoaded = false;
int size = activity.xmppConnectionService.loadMoreMessages(conversation, timestamp); Log.d(Config.LOGTAG,"load more messages");
conversation.populateWithMessages(ConversationFragment.this.messageList); activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() {
@Override
public void onMoreMessagesLoaded(final int count, Conversation conversation) {
if (ConversationFragment.this.conversation != conversation) {
return;
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
int firstItem = messagesView.getFirstVisiblePosition();
Log.d(Config.LOGTAG, "done loading more messages. first item: " + firstItem);
ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList);
updateStatusMessages(); updateStatusMessages();
messageListAdapter.notifyDataSetChanged(); messageListAdapter.notifyDataSetChanged();
if (size != 0) { if (count != 0) {
messagesLoaded = true; messagesLoaded = true;
} }
messagesView.setSelectionFromTop(size + 1, 0); messagesView.setSelectionFromTop(firstItem + count, 0);
}
});
}
});
} }
} }
} }
@ -580,6 +599,7 @@ public class ConversationFragment extends Fragment {
} }
} }
conversation.populateWithMessages(ConversationFragment.this.messageList); conversation.populateWithMessages(ConversationFragment.this.messageList);
this.messagesLoaded = this.messageList.size() > 0;
for (Message message : this.messageList) { for (Message message : this.messageList) {
if (message.getEncryption() == Message.ENCRYPTION_PGP if (message.getEncryption() == Message.ENCRYPTION_PGP
&& (message.getStatus() == Message.STATUS_RECEIVED || message && (message.getStatus() == Message.STATUS_RECEIVED || message