prevent previoulsly cleared messages from reloading. fixes #1110

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java          | 12 
src/main/java/eu/siacs/conversations/entities/Message.java               |  8 
src/main/java/eu/siacs/conversations/services/MessageArchiveService.java | 17 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java |  4 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java        |  6 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java        | 10 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java      | 42 
src/main/res/layout/message_status.xml                                   |  9 
src/main/res/values/strings.xml                                          |  1 
9 files changed, 92 insertions(+), 17 deletions(-)

Detailed changes

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

@@ -286,6 +286,14 @@ public class Conversation extends AbstractEntity implements Blockable {
 		return this.mFirstMamReference;
 	}
 
+	public void setLastClearHistory(long time) {
+		setAttribute("last_clear_history",String.valueOf(time));
+	}
+
+	public long getLastClearHistory() {
+		return getLongAttribute("last_clear_history", 0);
+	}
+
 	public interface OnMessageFound {
 		void onMessageFound(final Message message);
 	}
@@ -720,6 +728,10 @@ public class Conversation extends AbstractEntity implements Blockable {
 	}
 
 	public long getLastMessageTransmitted() {
+		long last_clear = getLastClearHistory();
+		if (last_clear != 0) {
+			return last_clear;
+		}
 		synchronized (this.messages) {
 			for(int i = this.messages.size() - 1; i >= 0; --i) {
 				Message message = this.messages.get(i);

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

@@ -178,6 +178,14 @@ public class Message extends AbstractEntity {
 		return message;
 	}
 
+	public static Message createLoadMoreMessage(Conversation conversation) {
+		final Message message = new Message();
+		message.setType(Message.TYPE_STATUS);
+		message.setConversation(conversation);
+		message.setBody("LOAD_MORE");
+		return message;
+	}
+
 	@Override
 	public ContentValues getContentValues() {
 		ContentValues values = new ContentValues();

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

@@ -24,13 +24,13 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 
 	private final XmppConnectionService mXmppConnectionService;
 
-	private final HashSet<Query> queries = new HashSet<Query>();
-	private final ArrayList<Query> pendingQueries = new ArrayList<Query>();
+	private final HashSet<Query> queries = new HashSet<>();
+	private final ArrayList<Query> pendingQueries = new ArrayList<>();
 
 	public enum PagingOrder {
 		NORMAL,
 		REVERSE
-	};
+	}
 
 	public MessageArchiveService(final XmppConnectionService service) {
 		this.mXmppConnectionService = service;
@@ -137,7 +137,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 						synchronized (MessageArchiveService.this.queries) {
 							MessageArchiveService.this.queries.remove(query);
 							if (query.hasCallback()) {
-								query.callback();
+								query.callback(false);
 							}
 						}
 					} else if (packet.getType() != IqPacket.TYPE.RESULT) {
@@ -169,7 +169,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 			}
 		}
 		if (query.hasCallback()) {
-			query.callback();
+			query.callback(done);
 		} else {
 			this.mXmppConnectionService.updateConversationUi();
 		}
@@ -329,10 +329,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 			this.callback = callback;
 		}
 
-		public void callback() {
+		public void callback(boolean done) {
 			if (this.callback != null) {
 				this.callback.onMoreMessagesLoaded(messageCount,conversation);
-				if (messageCount == 0) {
+				if (done) {
 					this.callback.informUser(R.string.no_more_history_on_server);
 				}
 			}
@@ -375,7 +375,8 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
 		public String toString() {
 			StringBuilder builder = new StringBuilder();
 			if (this.muc()) {
-				builder.append("to="+this.getWith().toString());
+				builder.append("to=");
+				builder.append(this.getWith().toString());
 			} else {
 				builder.append("with=");
 				if (this.getWith() == null) {

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

@@ -1215,6 +1215,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 	public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) {
 		if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
 			return;
+		} else if (timestamp == 0) {
+			callback.onMoreMessagesLoaded(0, conversation);
+			return;
 		}
 		Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp));
 		Runnable runnable = new Runnable() {
@@ -2911,6 +2914,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 	public void clearConversationHistory(final Conversation conversation) {
 		conversation.clearMessages();
 		conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
+		conversation.setLastClearHistory(System.currentTimeMillis());
 		Runnable runnable = new Runnable() {
 			@Override
 			public void run() {

src/main/java/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -1602,4 +1602,10 @@ public class ConversationActivity extends XmppActivity
 	public boolean highlightSelectedConversations() {
 		return !isConversationsOverviewHideable() || this.conversationWasSelectedByKeyboard;
 	}
+
+	public void setMessagesLoaded() {
+		if (mConversationFragment != null) {
+			mConversationFragment.setMessagesLoaded();
+		}
+	}
 }

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -40,12 +40,9 @@ import net.java.otr4j.session.SessionStatus;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.concurrent.ConcurrentLinkedQueue;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
-import eu.siacs.conversations.crypto.PgpEngine;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
@@ -318,6 +315,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 	private ConversationActivity activity;
 	private Message selectedMessage;
 
+	public void setMessagesLoaded() {
+		this.messagesLoaded = true;
+	}
+
 	private void sendMessage() {
 		final String body = mEditMessage.getText().toString();
 		if (body.length() == 0 || this.conversation == null) {
@@ -1008,6 +1009,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
 
 	protected void updateStatusMessages() {
 		synchronized (this.messageList) {
+			if (conversation.getLastClearHistory() != 0) {
+				this.messageList.add(0, Message.createLoadMoreMessage(conversation));
+			}
 			if (conversation.getMode() == Conversation.MODE_SINGLE) {
 				ChatState state = conversation.getIncomingChatState();
 				if (state == ChatState.COMPOSING) {

src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java 🔗

@@ -428,6 +428,19 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		viewHolder.image.setOnLongClickListener(openContextMenu);
 	}
 
+	private void loadMoreMessages(Conversation conversation) {
+		conversation.setLastClearHistory(0);
+		conversation.setHasMessagesLeftOnServer(true);
+		conversation.setFirstMamReference(null);
+		long timestamp = conversation.getLastMessageTransmitted();
+		if (timestamp == 0) {
+			timestamp = System.currentTimeMillis();
+		}
+		activity.setMessagesLoaded();
+		activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp);
+		Toast.makeText(activity, R.string.fetching_history_from_server,Toast.LENGTH_LONG).show();
+	}
+
 	@Override
 	public View getView(int position, View view, ViewGroup parent) {
 		final Message message = getItem(position);
@@ -484,6 +497,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 					view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
 					viewHolder.contact_picture = (ImageView) view.findViewById(R.id.message_photo);
 					viewHolder.status_message = (TextView) view.findViewById(R.id.status_message);
+					viewHolder.load_more_messages = (Button) view.findViewById(R.id.load_more_messages);
 					break;
 				default:
 					viewHolder = null;
@@ -500,16 +514,31 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		boolean darkBackground = (type == RECEIVED && (!isInValidSession || !mUseWhiteBackground));
 
 		if (type == STATUS) {
-			if (conversation.getMode() == Conversation.MODE_SINGLE) {
-				viewHolder.contact_picture.setImageBitmap(activity
-						.avatarService().get(conversation.getContact(),
-							activity.getPixel(32)));
-				viewHolder.contact_picture.setAlpha(0.5f);
+			if ("LOAD_MORE".equals(message.getBody())) {
+				viewHolder.status_message.setVisibility(View.GONE);
+				viewHolder.contact_picture.setVisibility(View.GONE);
+				viewHolder.load_more_messages.setVisibility(View.VISIBLE);
+				viewHolder.load_more_messages.setOnClickListener(new OnClickListener() {
+					@Override
+					public void onClick(View v) {
+						loadMoreMessages(message.getConversation());
+					}
+				});
+			} else {
+				viewHolder.status_message.setVisibility(View.VISIBLE);
+				viewHolder.contact_picture.setVisibility(View.VISIBLE);
+				viewHolder.load_more_messages.setVisibility(View.GONE);
+				if (conversation.getMode() == Conversation.MODE_SINGLE) {
+					viewHolder.contact_picture.setImageBitmap(activity
+							.avatarService().get(conversation.getContact(),
+									activity.getPixel(32)));
+					viewHolder.contact_picture.setAlpha(0.5f);
+				}
 				viewHolder.status_message.setText(message.getBody());
 			}
 			return view;
 		} else {
-			loadAvatar(message,viewHolder.contact_picture);
+			loadAvatar(message, viewHolder.contact_picture);
 		}
 
 		viewHolder.contact_picture
@@ -671,6 +700,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		protected ImageView contact_picture;
 		protected TextView status_message;
 		protected TextView encryption;
+		public Button load_more_messages;
 	}
 
 	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {

src/main/res/layout/message_status.xml 🔗

@@ -9,6 +9,15 @@
                 android:paddingRight="8dp"
                 android:paddingTop="5dp">
 
+    <Button
+        android:id="@+id/load_more_messages"
+        style="?android:attr/borderlessButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/load_more_messages"
+        android:textColor="@color/accent" android:layout_centerVertical="true"
+        android:layout_centerHorizontal="true"/>
+
     <com.makeramen.roundedimageview.RoundedImageView
         android:id="@+id/message_photo"
         android:layout_width="32dp"

src/main/res/values/strings.xml 🔗

@@ -560,6 +560,7 @@
 		<item quantity="one">%d message</item>
 		<item quantity="other">%d messages</item>
 	</plurals>
+	<string name="load_more_messages">Load more messages</string>
 	<string name="shared_file_with_x">Shared file with %s</string>
 	<string name="shared_image_with_x">Shared image with %s</string>
 	<string name="no_storage_permission">Conversations need access to external storage</string>