properly restore LMC edits. switch to LMC v1.1
fixes #3566 closes #3592
This commit is contained in:
parent
b3c00d7163
commit
cc79d8f6b3
|
@ -117,7 +117,7 @@ public final class Config {
|
||||||
public static final boolean IGNORE_ID_REWRITE_IN_MUC = true;
|
public static final boolean IGNORE_ID_REWRITE_IN_MUC = true;
|
||||||
public static final boolean MUC_LEAVE_BEFORE_JOIN = true;
|
public static final boolean MUC_LEAVE_BEFORE_JOIN = true;
|
||||||
|
|
||||||
public static final boolean USE_LMC_VERSION_1_1 = false;
|
public static final boolean USE_LMC_VERSION_1_1 = true;
|
||||||
|
|
||||||
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY * 5;
|
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY * 5;
|
||||||
public static final int MAM_MAX_MESSAGES = 750;
|
public static final int MAM_MAX_MESSAGES = 750;
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
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 Edit {
|
||||||
|
|
||||||
|
private final String editedId;
|
||||||
|
private final String serverMsgId;
|
||||||
|
|
||||||
|
Edit(String editedId, String serverMsgId) {
|
||||||
|
this.editedId = editedId;
|
||||||
|
this.serverMsgId = serverMsgId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toJson(List<Edit> edits) throws JSONException {
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
for (Edit edit : edits) {
|
||||||
|
jsonArray.put(edit.toJson());
|
||||||
|
}
|
||||||
|
return jsonArray.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean wasPreviouslyEditedRemoteMsgId(List<Edit> edits, String remoteMsgId) {
|
||||||
|
for (Edit edit : edits) {
|
||||||
|
if (edit.editedId != null && edit.editedId.equals(remoteMsgId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean wasPreviouslyEditedServerMsgId(List<Edit> edits, String serverMsgId) {
|
||||||
|
for (Edit edit : edits) {
|
||||||
|
if (edit.serverMsgId != null && edit.serverMsgId.equals(serverMsgId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Edit fromJson(JSONObject jsonObject) throws JSONException {
|
||||||
|
String edited = jsonObject.has("edited_id") ? jsonObject.getString("edited_id") : null;
|
||||||
|
String serverMsgId = jsonObject.has("server_msg_id") ? jsonObject.getString("server_msg_id") : null;
|
||||||
|
return new Edit(edited, serverMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Edit> fromJson(String input) {
|
||||||
|
final ArrayList<Edit> list = new ArrayList<>();
|
||||||
|
if (input == null) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final JSONArray jsonArray = new JSONArray(input);
|
||||||
|
for (int i = 0; i < jsonArray.length(); ++i) {
|
||||||
|
list.add(fromJson(jsonArray.getJSONObject(i)));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject toJson() throws JSONException {
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("edited_id", editedId);
|
||||||
|
jsonObject.put("server_msg_id", serverMsgId);
|
||||||
|
return jsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getEditedId() {
|
||||||
|
return editedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
Edit edit = (Edit) o;
|
||||||
|
|
||||||
|
if (editedId != null ? !editedId.equals(edit.editedId) : edit.editedId != null)
|
||||||
|
return false;
|
||||||
|
return serverMsgId != null ? serverMsgId.equals(edit.serverMsgId) : edit.serverMsgId == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = editedId != null ? editedId.hashCode() : 0;
|
||||||
|
result = 31 * result + (serverMsgId != null ? serverMsgId.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,80 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -97,7 +97,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
protected boolean deleted = false;
|
protected boolean deleted = false;
|
||||||
protected boolean carbon = false;
|
protected boolean carbon = false;
|
||||||
protected boolean oob = false;
|
protected boolean oob = false;
|
||||||
protected List<Edited> edits = new ArrayList<>();
|
protected List<Edit> edits = new ArrayList<>();
|
||||||
protected String relativeFilePath;
|
protected String relativeFilePath;
|
||||||
protected boolean read = true;
|
protected boolean read = true;
|
||||||
protected String remoteMsgId = null;
|
protected String remoteMsgId = null;
|
||||||
|
@ -174,7 +174,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
this.serverMsgId = serverMsgId;
|
this.serverMsgId = serverMsgId;
|
||||||
this.axolotlFingerprint = fingerprint;
|
this.axolotlFingerprint = fingerprint;
|
||||||
this.read = read;
|
this.read = read;
|
||||||
this.edits = Edited.fromJson(edited);
|
this.edits = Edit.fromJson(edited);
|
||||||
this.oob = oob;
|
this.oob = oob;
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
this.readByMarkers = readByMarkers == null ? new HashSet<>() : readByMarkers;
|
this.readByMarkers = readByMarkers == null ? new HashSet<>() : readByMarkers;
|
||||||
|
@ -263,7 +263,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
values.put(FINGERPRINT, axolotlFingerprint);
|
values.put(FINGERPRINT, axolotlFingerprint);
|
||||||
values.put(READ, read ? 1 : 0);
|
values.put(READ, read ? 1 : 0);
|
||||||
try {
|
try {
|
||||||
values.put(EDITED, Edited.toJson(edits));
|
values.put(EDITED, Edit.toJson(edits));
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(Config.LOGTAG,"error persisting json for edits",e);
|
Log.e(Config.LOGTAG,"error persisting json for edits",e);
|
||||||
}
|
}
|
||||||
|
@ -434,11 +434,14 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putEdited(String edited, String serverMsgId) {
|
public void putEdited(String edited, String serverMsgId) {
|
||||||
this.edits.add(new Edited(edited, serverMsgId));
|
final Edit edit = new Edit(edited, serverMsgId);
|
||||||
|
if (this.edits.size() < 128 && !this.edits.contains(edit)) {
|
||||||
|
this.edits.add(edit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean remoteMsgIdMatchInEdit(String id) {
|
boolean remoteMsgIdMatchInEdit(String id) {
|
||||||
for(Edited edit : this.edits) {
|
for(Edit edit : this.edits) {
|
||||||
if (id.equals(edit.getEditedId())) {
|
if (id.equals(edit.getEditedId())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -507,8 +510,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
|
|
||||||
boolean similar(Message message) {
|
boolean similar(Message message) {
|
||||||
if (!isPrivateMessage() && this.serverMsgId != null && message.getServerMsgId() != null) {
|
if (!isPrivateMessage() && this.serverMsgId != null && message.getServerMsgId() != null) {
|
||||||
return this.serverMsgId.equals(message.getServerMsgId()) || Edited.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId());
|
return this.serverMsgId.equals(message.getServerMsgId()) || Edit.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId());
|
||||||
} else if (Edited.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId())) {
|
} else if (Edit.wasPreviouslyEditedServerMsgId(edits, message.getServerMsgId())) {
|
||||||
return true;
|
return true;
|
||||||
} else if (this.body == null || this.counterpart == null) {
|
} else if (this.body == null || this.counterpart == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -524,7 +527,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
final boolean matchingCounterpart = this.counterpart.equals(message.getCounterpart());
|
final boolean matchingCounterpart = this.counterpart.equals(message.getCounterpart());
|
||||||
if (message.getRemoteMsgId() != null) {
|
if (message.getRemoteMsgId() != null) {
|
||||||
final boolean hasUuid = CryptoHelper.UUID_PATTERN.matcher(message.getRemoteMsgId()).matches();
|
final boolean hasUuid = CryptoHelper.UUID_PATTERN.matcher(message.getRemoteMsgId()).matches();
|
||||||
if (hasUuid && matchingCounterpart && Edited.wasPreviouslyEditedRemoteMsgId(edits, message.getRemoteMsgId())) {
|
if (hasUuid && matchingCounterpart && Edit.wasPreviouslyEditedRemoteMsgId(edits, message.getRemoteMsgId())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
|
return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
|
||||||
|
|
|
@ -63,7 +63,7 @@ import rocks.xmpp.addr.Jid;
|
||||||
public class DatabaseBackend extends SQLiteOpenHelper {
|
public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "history";
|
private static final String DATABASE_NAME = "history";
|
||||||
private static final int DATABASE_VERSION = 45;
|
private static final int DATABASE_VERSION = 46;
|
||||||
private static DatabaseBackend instance = null;
|
private static DatabaseBackend instance = null;
|
||||||
private static String CREATE_CONTATCS_STATEMENT = "create table "
|
private static String CREATE_CONTATCS_STATEMENT = "create table "
|
||||||
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
|
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
|
||||||
|
@ -546,6 +546,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
if (oldVersion < 45 && newVersion >= 45) {
|
if (oldVersion < 45 && newVersion >= 45) {
|
||||||
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.BODY_LANGUAGE);
|
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.BODY_LANGUAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 46 && newVersion >= 46) {
|
||||||
|
db.execSQL("update "+Message.TABLENAME+" set "+Message.EDITED+"=NULL");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void canonicalizeJids(SQLiteDatabase db) {
|
private void canonicalizeJids(SQLiteDatabase db) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class CursorUtils {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
||||||
if (cursor instanceof AbstractWindowedCursor) {
|
if (cursor instanceof AbstractWindowedCursor) {
|
||||||
final AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
|
final AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
|
||||||
windowedCursor.setWindow(new CursorWindow("8M", 8 * 1024 * 1024));
|
windowedCursor.setWindow(new CursorWindow("4M", 8 * 1024 * 1024));
|
||||||
}
|
}
|
||||||
if (cursor instanceof SQLiteCursor) {
|
if (cursor instanceof SQLiteCursor) {
|
||||||
((SQLiteCursor) cursor).setFillWindowForwardOnly(true);
|
((SQLiteCursor) cursor).setFillWindowForwardOnly(true);
|
||||||
|
|
Loading…
Reference in New Issue