keep track of previously edited ids

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Edited.java                | 80 
src/main/java/eu/siacs/conversations/entities/Message.java               | 34 
src/main/java/eu/siacs/conversations/parser/MessageParser.java           |  2 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java    |  5 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 12 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java        |  3 
6 files changed, 118 insertions(+), 18 deletions(-)

Detailed changes

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

@@ -0,0 +1,80 @@
+package eu.siacs.conversations.entities;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Edited {
+
+    private final String editedId;
+    private final String serverMsgId;
+
+    public Edited(String editedId, String serverMsgId) {
+        this.editedId = editedId;
+        this.serverMsgId = serverMsgId;
+    }
+
+    public static String toJson(List<Edited> edits) throws JSONException {
+        JSONArray jsonArray = new JSONArray();
+        for (Edited edited : edits) {
+            jsonArray.put(edited.toJson());
+        }
+        return jsonArray.toString();
+    }
+
+    public static boolean wasPreviouslyEditedRemoteMsgId(List<Edited> editeds, String remoteMsgId) {
+        for (Edited edited : editeds) {
+            if (edited.editedId != null && edited.editedId.equals(remoteMsgId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean wasPreviouslyEditedServerMsgId(List<Edited> editeds, String serverMsgId) {
+        for (Edited edited : editeds) {
+            if (edited.serverMsgId != null && edited.serverMsgId.equals(serverMsgId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static Edited fromJson(JSONObject jsonObject) throws JSONException {
+        String edited = jsonObject.getString("edited_id");
+        String serverMsgId = jsonObject.getString("server_msg_id");
+        return new Edited(edited, serverMsgId);
+    }
+
+    public static List<Edited> fromJson(String input) {
+        ArrayList<Edited> list = new ArrayList<>();
+        if (input == null) {
+            return list;
+        }
+        try {
+            JSONArray jsonArray = new JSONArray(input);
+            for (int i = 0; i < jsonArray.length(); ++i) {
+                list.add(fromJson(jsonArray.getJSONObject(i)));
+            }
+
+        } catch (JSONException e) {
+            list = new ArrayList<>();
+            list.add(new Edited(input, null));
+        }
+        return list;
+    }
+
+    public JSONObject toJson() throws JSONException {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("edited_id", editedId);
+        jsonObject.put("server_msg_id", serverMsgId);
+        return jsonObject;
+    }
+
+    public String getEditedId() {
+        return editedId;
+    }
+}

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

@@ -3,10 +3,14 @@ package eu.siacs.conversations.entities;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.text.SpannableStringBuilder;
+import android.util.Log;
+
+import org.json.JSONException;
 
 import java.lang.ref.WeakReference;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -86,7 +90,7 @@ public class Message extends AbstractEntity {
 	protected int type;
 	protected boolean carbon = false;
 	protected boolean oob = false;
-	protected String edited = null;
+	protected List<Edited> edits = new ArrayList<>();
 	protected String relativeFilePath;
 	protected boolean read = true;
 	protected String remoteMsgId = null;
@@ -160,10 +164,10 @@ public class Message extends AbstractEntity {
 		this.serverMsgId = serverMsgId;
 		this.axolotlFingerprint = fingerprint;
 		this.read = read;
-		this.edited = edited;
+		this.edits = Edited.fromJson(edited);
 		this.oob = oob;
 		this.errorMessage = errorMessage;
-		this.readByMarkers = readByMarkers == null ? new HashSet<ReadByMarker>() : readByMarkers;
+		this.readByMarkers = readByMarkers == null ? new HashSet<>() : readByMarkers;
 		this.markable = markable;
 	}
 
@@ -256,7 +260,11 @@ public class Message extends AbstractEntity {
 		values.put(SERVER_MSG_ID, serverMsgId);
 		values.put(FINGERPRINT, axolotlFingerprint);
 		values.put(READ, read ? 1 : 0);
-		values.put(EDITED, edited);
+		try {
+			values.put(EDITED, Edited.toJson(edits));
+		} catch (JSONException e) {
+			Log.e(Config.LOGTAG,"error persisting json for edits",e);
+		}
 		values.put(OOB, oob ? 1 : 0);
 		values.put(ERROR_MESSAGE, errorMessage);
 		values.put(READ_BY_MARKERS, ReadByMarker.toJson(readByMarkers).toString());
@@ -413,12 +421,12 @@ public class Message extends AbstractEntity {
 		this.carbon = carbon;
 	}
 
-	public void setEdited(String edited) {
-		this.edited = edited;
+	public void putEdited(String edited, String serverMsgId) {
+		this.edits.add(new Edited(edited, serverMsgId));
 	}
 
 	public boolean edited() {
-		return this.edited != null;
+		return this.edits.size() > 0;
 	}
 
 	public void setTrueCounterpart(Jid trueCounterpart) {
@@ -470,7 +478,9 @@ public class Message extends AbstractEntity {
 
 	public boolean similar(Message message) {
 		if (type != TYPE_PRIVATE && this.serverMsgId != null && message.getServerMsgId() != null) {
-			return this.serverMsgId.equals(message.getServerMsgId());
+			return this.serverMsgId.equals(message.getServerMsgId()) || Edited.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId());
+		} else if (Edited.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId())) {
+			return true;
 		} else if (this.body == null || this.counterpart == null) {
 			return false;
 		} else {
@@ -485,7 +495,7 @@ public class Message extends AbstractEntity {
 			final boolean matchingCounterpart = this.counterpart.equals(message.getCounterpart());
 			if (message.getRemoteMsgId() != null) {
 				final boolean hasUuid = CryptoHelper.UUID_PATTERN.matcher(message.getRemoteMsgId()).matches();
-				if (hasUuid && this.edited != null && matchingCounterpart && this.edited.equals(message.getRemoteMsgId())) {
+				if (hasUuid && matchingCounterpart && Edited.wasPreviouslyEditedRemoteMsgId(edits, message.getRemoteMsgId())) {
 					return true;
 				}
 				return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
@@ -686,7 +696,11 @@ public class Message extends AbstractEntity {
 	}
 
 	public String getEditedId() {
-		return edited;
+		if (edits.size() > 0) {
+			return edits.get(edits.size() - 1).getEditedId();
+		} else {
+			throw new IllegalStateException("Attempting to store unedited message");
+		}
 	}
 
 	public void setOob(boolean isOob) {

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

@@ -518,7 +518,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
                             final String uuid = replacedMessage.getUuid();
                             replacedMessage.setUuid(UUID.randomUUID().toString());
                             replacedMessage.setBody(message.getBody());
-                            replacedMessage.setEdited(replacedMessage.getRemoteMsgId());
+                            replacedMessage.putEdited(replacedMessage.getRemoteMsgId(), replacedMessage.getServerMsgId());
                             replacedMessage.setRemoteMsgId(remoteMsgId);
                             if (replacedMessage.getServerMsgId() == null || message.getServerMsgId() != null) {
                                 replacedMessage.setServerMsgId(message.getServerMsgId());

src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java 🔗

@@ -902,11 +902,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 		return db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args) == 1;
 	}
 
-	public void updateMessage(Message message, String uuid) {
+	public boolean updateMessage(Message message, String uuid) {
 		SQLiteDatabase db = this.getWritableDatabase();
 		String[] args = {uuid};
-		db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
-				+ "=?", args);
+		return db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args) == 1;
 	}
 
 	public void readRoster(Roster roster) {

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

@@ -1316,7 +1316,9 @@ public class XmppConnectionService extends Service {
                         if (message.edited()) {
                             message.setBody(decryptedBody);
                             message.setEncryption(Message.ENCRYPTION_DECRYPTED);
-                            databaseBackend.updateMessage(message, message.getEditedId());
+                            if (!databaseBackend.updateMessage(message, message.getEditedId())) {
+                                Log.e(Config.LOGTAG,"error updated message in DB after edit");
+                            }
                             updateConversationUi();
                             return;
                         } else {
@@ -1354,7 +1356,9 @@ public class XmppConnectionService extends Service {
             if (saveInDb) {
                 databaseBackend.createMessage(message);
             } else if (message.edited()) {
-                databaseBackend.updateMessage(message, message.getEditedId());
+                if (!databaseBackend.updateMessage(message, message.getEditedId())) {
+                    Log.e(Config.LOGTAG,"error updated message in DB after edit");
+                }
             }
             updateConversationUi();
         }
@@ -2825,7 +2829,9 @@ public class XmppConnectionService extends Service {
 	}
 
 	public void updateMessage(Message message, String uuid) {
-		databaseBackend.updateMessage(message, uuid);
+		if (!databaseBackend.updateMessage(message, uuid)) {
+            Log.e(Config.LOGTAG,"error updated message in DB after edit");
+        }
 		updateConversationUi();
 	}
 

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

@@ -746,7 +746,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
         } else {
             message = conversation.getCorrectingMessage();
             message.setBody(body);
-            message.setEdited(message.getUuid());
+            message.putEdited(message.getUuid(), message.getServerMsgId());
+            message.setServerMsgId(null);
             message.setUuid(UUID.randomUUID().toString());
         }
         switch (conversation.getNextEncryption()) {