Add option to export messages in plain text + rebuild database backend
This commit is contained in:
parent
2e2b13fc72
commit
61fbfed818
7 changed files with 334 additions and 246 deletions
|
@ -132,6 +132,13 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
public static final String OCCUPANT_ID = "occupantId";
|
public static final String OCCUPANT_ID = "occupantId";
|
||||||
public static final String REACTIONS = "reactions";
|
public static final String REACTIONS = "reactions";
|
||||||
public static final String ME_COMMAND = "/me ";
|
public static final String ME_COMMAND = "/me ";
|
||||||
|
public static final String PAYLOADS = "payloads";
|
||||||
|
public static final String TIME_RECEIVED = "timeReceived";
|
||||||
|
public static final String SUBJECT = "subject";
|
||||||
|
public static final String FILE_PARAMS = "fileParams";
|
||||||
|
public static final String OCCUPANTID = "occupant_id";
|
||||||
|
public static final String NOTIFICATION_DISMISSED = "notificationDismissed";
|
||||||
|
|
||||||
|
|
||||||
public static final String ERROR_MESSAGE_CANCELLED = "eu.siacs.conversations.cancelled";
|
public static final String ERROR_MESSAGE_CANCELLED = "eu.siacs.conversations.cancelled";
|
||||||
|
|
||||||
|
@ -288,7 +295,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Message fromCursor(Cursor cursor, Conversation conversation) throws IOException {
|
public static Message fromCursor(Cursor cursor, Conversation conversation) throws IOException {
|
||||||
String payloadsStr = cursor.getString(cursor.getColumnIndex("payloads"));
|
String payloadsStr = cursor.getString(cursor.getColumnIndexOrThrow(PAYLOADS));
|
||||||
List<Element> payloads = new ArrayList<>();
|
List<Element> payloads = new ArrayList<>();
|
||||||
if (payloadsStr != null) {
|
if (payloadsStr != null) {
|
||||||
final XmlReader xmlReader = new XmlReader();
|
final XmlReader xmlReader = new XmlReader();
|
||||||
|
@ -302,40 +309,39 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
Log.e(Config.LOGTAG, "Failed to parse: " + payloadsStr, e);
|
Log.e(Config.LOGTAG, "Failed to parse: " + payloadsStr, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Message m = new Message(conversation,
|
Message m = new Message(conversation,
|
||||||
cursor.getString(cursor.getColumnIndex(UUID)),
|
cursor.getString(cursor.getColumnIndexOrThrow(UUID)),
|
||||||
cursor.getString(cursor.getColumnIndex(CONVERSATION)),
|
cursor.getString(cursor.getColumnIndexOrThrow(CONVERSATION)),
|
||||||
fromString(cursor.getString(cursor.getColumnIndex(COUNTERPART))),
|
fromString(cursor.getString(cursor.getColumnIndexOrThrow(COUNTERPART))),
|
||||||
fromString(cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART))),
|
fromString(cursor.getString(cursor.getColumnIndexOrThrow(TRUE_COUNTERPART))),
|
||||||
cursor.getString(cursor.getColumnIndex(BODY)),
|
cursor.getString(cursor.getColumnIndexOrThrow(BODY)),
|
||||||
cursor.getLong(cursor.getColumnIndex(TIME_SENT)),
|
cursor.getLong(cursor.getColumnIndexOrThrow(TIME_SENT)),
|
||||||
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
|
cursor.getInt(cursor.getColumnIndexOrThrow(ENCRYPTION)),
|
||||||
cursor.getInt(cursor.getColumnIndex(STATUS)),
|
cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)),
|
||||||
cursor.getInt(cursor.getColumnIndex(TYPE)),
|
cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
|
||||||
cursor.getInt(cursor.getColumnIndex(CARBON)) > 0,
|
cursor.getInt(cursor.getColumnIndexOrThrow(CARBON)) > 0,
|
||||||
cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
|
cursor.getString(cursor.getColumnIndexOrThrow(REMOTE_MSG_ID)),
|
||||||
cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
|
cursor.getString(cursor.getColumnIndexOrThrow(RELATIVE_FILE_PATH)),
|
||||||
cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)),
|
cursor.getString(cursor.getColumnIndexOrThrow(SERVER_MSG_ID)),
|
||||||
cursor.getString(cursor.getColumnIndex(FINGERPRINT)),
|
cursor.getString(cursor.getColumnIndexOrThrow(FINGERPRINT)),
|
||||||
cursor.getInt(cursor.getColumnIndex(READ)) > 0,
|
cursor.getInt(cursor.getColumnIndexOrThrow(READ)) > 0,
|
||||||
cursor.getString(cursor.getColumnIndex(EDITED)),
|
cursor.getString(cursor.getColumnIndexOrThrow(EDITED)),
|
||||||
cursor.getInt(cursor.getColumnIndex(OOB)) > 0,
|
cursor.getInt(cursor.getColumnIndexOrThrow(OOB)) > 0,
|
||||||
cursor.getString(cursor.getColumnIndex(ERROR_MESSAGE)),
|
cursor.getString(cursor.getColumnIndexOrThrow(ERROR_MESSAGE)),
|
||||||
ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndex(READ_BY_MARKERS))),
|
ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndexOrThrow(READ_BY_MARKERS))),
|
||||||
cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0,
|
cursor.getInt(cursor.getColumnIndexOrThrow(MARKABLE)) > 0,
|
||||||
cursor.getInt(cursor.getColumnIndex(DELETED)) > 0,
|
cursor.getInt(cursor.getColumnIndexOrThrow(DELETED)) > 0,
|
||||||
cursor.getString(cursor.getColumnIndex(BODY_LANGUAGE)),
|
cursor.getString(cursor.getColumnIndexOrThrow(BODY_LANGUAGE)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(OCCUPANT_ID)),
|
cursor.getString(cursor.getColumnIndexOrThrow(OCCUPANT_ID)),
|
||||||
Reaction.fromString(cursor.getString(cursor.getColumnIndexOrThrow(REACTIONS))),
|
Reaction.fromString(cursor.getString(cursor.getColumnIndexOrThrow(REACTIONS))),
|
||||||
cursor.getLong(cursor.getColumnIndex(cursor.isNull(cursor.getColumnIndex("timeReceived")) ? TIME_SENT : "timeReceived")),
|
cursor.getLong(cursor.getColumnIndexOrThrow(cursor.isNull(cursor.getColumnIndexOrThrow(TIME_RECEIVED)) ? TIME_SENT : TIME_RECEIVED)),
|
||||||
cursor.getString(cursor.getColumnIndex("subject")),
|
cursor.getString(cursor.getColumnIndexOrThrow(SUBJECT)),
|
||||||
cursor.getString(cursor.getColumnIndex("fileParams")),
|
cursor.getString(cursor.getColumnIndexOrThrow(FILE_PARAMS)),
|
||||||
payloads
|
payloads
|
||||||
);
|
);
|
||||||
final var legacyOccupant = cursor.getString(cursor.getColumnIndex("occupant_id"));
|
final var legacyOccupant = cursor.getString(cursor.getColumnIndexOrThrow(OCCUPANTID));
|
||||||
if (legacyOccupant != null) m.setOccupantId(legacyOccupant);
|
if (legacyOccupant != null) m.setOccupantId(legacyOccupant);
|
||||||
if (cursor.getInt(cursor.getColumnIndex("notificationDismissed")) > 0) m.markNotificationDismissed();
|
if (cursor.getInt(cursor.getColumnIndexOrThrow(NOTIFICATION_DISMISSED)) > 0) m.markNotificationDismissed();
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,29 +371,9 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentValues getmonoclesContentValues() {
|
|
||||||
final FileParams fp = fileParams;
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(UUID, uuid);
|
|
||||||
values.put("subject", subject);
|
|
||||||
values.put("fileParams", fp == null ? null : fp.toString());
|
|
||||||
if (fp != null && !fp.isEmpty()) {
|
|
||||||
List<Element> sims = getSims();
|
|
||||||
if (sims.isEmpty()) {
|
|
||||||
addPayload(fp.toSims());
|
|
||||||
} else {
|
|
||||||
sims.get(0).replaceChildren(fp.toSims().getChildren());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values.put("payloads", payloads.size() < 1 ? null : payloads.stream().map(Object::toString).collect(Collectors.joining()));
|
|
||||||
values.put("occupant_id", occupantId);
|
|
||||||
values.put("timeReceived", timeReceived);
|
|
||||||
values.put("notificationDismissed", notificationDismissed ? 1 : 0);
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
|
final FileParams fp = fileParams;
|
||||||
final var values = new ContentValues();
|
final var values = new ContentValues();
|
||||||
values.put(UUID, uuid);
|
values.put(UUID, uuid);
|
||||||
values.put(CONVERSATION, conversationUuid);
|
values.put(CONVERSATION, conversationUuid);
|
||||||
|
@ -425,6 +411,20 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
||||||
values.put(BODY_LANGUAGE, bodyLanguage);
|
values.put(BODY_LANGUAGE, bodyLanguage);
|
||||||
values.put(OCCUPANT_ID, occupantId);
|
values.put(OCCUPANT_ID, occupantId);
|
||||||
values.put(REACTIONS, Reaction.toString(this.reactions));
|
values.put(REACTIONS, Reaction.toString(this.reactions));
|
||||||
|
values.put(SUBJECT, subject);
|
||||||
|
values.put(FILE_PARAMS, fp == null ? null : fp.toString());
|
||||||
|
if (fp != null && !fp.isEmpty()) {
|
||||||
|
List<Element> sims = getSims();
|
||||||
|
if (sims.isEmpty()) {
|
||||||
|
addPayload(fp.toSims());
|
||||||
|
} else {
|
||||||
|
sims.get(0).replaceChildren(fp.toSims().getChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.put(PAYLOADS, payloads.size() < 1 ? null : payloads.stream().map(Object::toString).collect(Collectors.joining()));
|
||||||
|
values.put(OCCUPANTID, occupantId);
|
||||||
|
values.put(TIME_RECEIVED, timeReceived);
|
||||||
|
values.put(NOTIFICATION_DISMISSED, notificationDismissed ? 1 : 0);
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,8 +41,10 @@ import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
@ -76,7 +78,7 @@ import eu.siacs.conversations.xmpp.mam.MamReference;
|
||||||
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 = 60;
|
private static final int DATABASE_VERSION = 61;
|
||||||
|
|
||||||
private static boolean requiresMessageIndexRebuild = false;
|
private static boolean requiresMessageIndexRebuild = false;
|
||||||
private static DatabaseBackend instance = null;
|
private static DatabaseBackend instance = null;
|
||||||
|
@ -225,164 +227,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void monoclesMigrate(SQLiteDatabase db) {
|
|
||||||
db.beginTransaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Cursor cursor = db.rawQuery("PRAGMA monocles.user_version", null);
|
|
||||||
cursor.moveToNext();
|
|
||||||
int monoclesVersion = cursor.getInt(0);
|
|
||||||
cursor.close();
|
|
||||||
|
|
||||||
if(monoclesVersion < 1) {
|
|
||||||
// No cross-DB foreign keys unfortunately
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE monocles." + Message.TABLENAME + "(" +
|
|
||||||
Message.UUID + " TEXT PRIMARY KEY, " +
|
|
||||||
"subject TEXT" +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 2) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles." + Message.TABLENAME + " " +
|
|
||||||
"ADD COLUMN oobUri TEXT"
|
|
||||||
);
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles." + Message.TABLENAME + " " +
|
|
||||||
"ADD COLUMN fileParams TEXT"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 2");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 3) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles." + Message.TABLENAME + " " +
|
|
||||||
"ADD COLUMN payloads TEXT"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 3");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 4) {
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE monocles.cids (" +
|
|
||||||
"cid TEXT NOT NULL PRIMARY KEY," +
|
|
||||||
"path TEXT NOT NULL" +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 4");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 5) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles." + Message.TABLENAME + " " +
|
|
||||||
"ADD COLUMN timeReceived NUMBER"
|
|
||||||
);
|
|
||||||
db.execSQL("CREATE INDEX monocles.message_time_received_index ON " + Message.TABLENAME + " (timeReceived)");
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 5");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 6) {
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE monocles.blocked_media (" +
|
|
||||||
"cid TEXT NOT NULL PRIMARY KEY" +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 6");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 7) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles.cids " +
|
|
||||||
"ADD COLUMN url TEXT"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 7");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 8) {
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE monocles.webxdc_updates (" +
|
|
||||||
"serial INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
|
||||||
Message.CONVERSATION + " TEXT NOT NULL, " +
|
|
||||||
"sender TEXT NOT NULL, " +
|
|
||||||
"thread TEXT NOT NULL, " +
|
|
||||||
"threadParent TEXT, " +
|
|
||||||
"info TEXT, " +
|
|
||||||
"document TEXT, " +
|
|
||||||
"summary TEXT, " +
|
|
||||||
"payload TEXT" +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
db.execSQL("CREATE INDEX monocles.webxdc_index ON webxdc_updates (" + Message.CONVERSATION + ", thread)");
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 8");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 9) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles.webxdc_updates " +
|
|
||||||
"ADD COLUMN message_id TEXT"
|
|
||||||
);
|
|
||||||
db.execSQL("CREATE UNIQUE INDEX monocles.webxdc_message_id_index ON webxdc_updates (" + Message.CONVERSATION + ", message_id)");
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 9");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 10) {
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE monocles.muted_participants (" +
|
|
||||||
"muc_jid TEXT NOT NULL, " +
|
|
||||||
"occupant_id TEXT NOT NULL, " +
|
|
||||||
"nick TEXT NOT NULL," +
|
|
||||||
"PRIMARY KEY (muc_jid, occupant_id)" +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles." + Message.TABLENAME + " " +
|
|
||||||
"ADD COLUMN occupant_id TEXT"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 10");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 11) {
|
|
||||||
if (Build.VERSION.SDK_INT >= 34) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles.muted_participants " +
|
|
||||||
"DROP COLUMN nick"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
db.execSQL("DROP TABLE monocles.muted_participants");
|
|
||||||
db.execSQL(
|
|
||||||
"CREATE TABLE monocles.muted_participants (" +
|
|
||||||
"muc_jid TEXT NOT NULL, " +
|
|
||||||
"occupant_id TEXT NOT NULL, " +
|
|
||||||
"PRIMARY KEY (muc_jid, occupant_id)" +
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 11");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(monoclesVersion < 12) {
|
|
||||||
db.execSQL(
|
|
||||||
"ALTER TABLE monocles." + Message.TABLENAME + " " +
|
|
||||||
"ADD COLUMN notificationDismissed NUMBER DEFAULT 0"
|
|
||||||
);
|
|
||||||
db.execSQL("PRAGMA monocles.user_version = 12");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigure(SQLiteDatabase db) {
|
public void onConfigure(SQLiteDatabase db) {
|
||||||
db.execSQL("PRAGMA foreign_keys=ON");
|
db.execSQL("PRAGMA foreign_keys=ON");
|
||||||
db.rawQuery("PRAGMA secure_delete=ON", null).close();
|
db.rawQuery("PRAGMA secure_delete=ON", null).close();
|
||||||
db.execSQL("ATTACH DATABASE ? AS monocles", new Object[]{context.getDatabasePath("monocles").getPath()});
|
|
||||||
monoclesMigrate(db);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -777,7 +625,96 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OCCUPANT_ID + " TEXT");
|
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OCCUPANT_ID + " TEXT");
|
||||||
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.REACTIONS + " TEXT");
|
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.REACTIONS + " TEXT");
|
||||||
}
|
}
|
||||||
}
|
if (oldVersion < 61 && newVersion >= 61) {
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " +
|
||||||
|
Message.SUBJECT + " TEXT"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " " +
|
||||||
|
"ADD COLUMN oobUri TEXT"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " " +
|
||||||
|
"ADD COLUMN fileParams TEXT"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " " +
|
||||||
|
"ADD COLUMN payloads TEXT"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE cids (" +
|
||||||
|
"cid TEXT NOT NULL PRIMARY KEY," +
|
||||||
|
"path TEXT NOT NULL" +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " " +
|
||||||
|
"ADD COLUMN timeReceived NUMBER"
|
||||||
|
);
|
||||||
|
db.execSQL("CREATE INDEX message_time_received_index ON " + Message.TABLENAME + " (timeReceived)");
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE blocked_media (" +
|
||||||
|
"cid TEXT NOT NULL PRIMARY KEY" +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE cids " +
|
||||||
|
"ADD COLUMN url TEXT"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE webxdc_updates (" +
|
||||||
|
"serial INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
Message.CONVERSATION + " TEXT NOT NULL, " +
|
||||||
|
"sender TEXT NOT NULL, " +
|
||||||
|
"thread TEXT NOT NULL, " +
|
||||||
|
"threadParent TEXT, " +
|
||||||
|
"info TEXT, " +
|
||||||
|
"document TEXT, " +
|
||||||
|
"summary TEXT, " +
|
||||||
|
"payload TEXT" +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
db.execSQL("CREATE INDEX webxdc_index ON webxdc_updates (" + Message.CONVERSATION + ", thread)");
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE webxdc_updates " +
|
||||||
|
"ADD COLUMN message_id TEXT"
|
||||||
|
);
|
||||||
|
db.execSQL("CREATE UNIQUE INDEX webxdc_message_id_index ON webxdc_updates (" + Message.CONVERSATION + ", message_id)");
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE muted_participants (" +
|
||||||
|
"muc_jid TEXT NOT NULL, " +
|
||||||
|
"occupant_id TEXT NOT NULL, " +
|
||||||
|
"nick TEXT NOT NULL," +
|
||||||
|
"PRIMARY KEY (muc_jid, occupant_id)" +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " " +
|
||||||
|
"ADD COLUMN occupant_id TEXT"
|
||||||
|
);
|
||||||
|
if (Build.VERSION.SDK_INT >= 34) {
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE muted_participants " +
|
||||||
|
"DROP COLUMN nick"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
db.execSQL("DROP TABLE muted_participants");
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE muted_participants (" +
|
||||||
|
"muc_jid TEXT NOT NULL, " +
|
||||||
|
"occupant_id TEXT NOT NULL, " +
|
||||||
|
"PRIMARY KEY (muc_jid, occupant_id)" +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
db.execSQL(
|
||||||
|
"ALTER TABLE " + Message.TABLENAME + " " +
|
||||||
|
"ADD COLUMN notificationDismissed NUMBER DEFAULT 0"
|
||||||
|
);
|
||||||
|
requiresMessageIndexRebuild = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void canonicalizeJids(SQLiteDatabase db) {
|
private void canonicalizeJids(SQLiteDatabase db) {
|
||||||
// migrate db to new, canonicalized JID domainpart representation
|
// migrate db to new, canonicalized JID domainpart representation
|
||||||
|
@ -862,7 +799,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
if (cid == null) return null;
|
if (cid == null) return null;
|
||||||
|
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
Cursor cursor = db.query("monocles.cids", new String[]{"path"}, "cid=?", new String[]{cid.toString()}, null, null, null);
|
Cursor cursor = db.query("cids", new String[]{"path"}, "cid=?", new String[]{cid.toString()}, null, null, null);
|
||||||
DownloadableFile f = null;
|
DownloadableFile f = null;
|
||||||
if (cursor.moveToNext()) {
|
if (cursor.moveToNext()) {
|
||||||
f = new DownloadableFile(cursor.getString(0));
|
f = new DownloadableFile(cursor.getString(0));
|
||||||
|
@ -873,7 +810,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public String getUrlForCid(Cid cid) {
|
public String getUrlForCid(Cid cid) {
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
Cursor cursor = db.query("monocles.cids", new String[]{"url"}, "cid=?", new String[]{cid.toString()}, null, null, null);
|
Cursor cursor = db.query("cids", new String[]{"url"}, "cid=?", new String[]{cid.toString()}, null, null, null);
|
||||||
String url = null;
|
String url = null;
|
||||||
if (cursor.moveToNext()) {
|
if (cursor.moveToNext()) {
|
||||||
url = cursor.getString(0);
|
url = cursor.getString(0);
|
||||||
|
@ -892,8 +829,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
cv.put("cid", cid.toString());
|
cv.put("cid", cid.toString());
|
||||||
if (file != null) cv.put("path", file.getAbsolutePath());
|
if (file != null) cv.put("path", file.getAbsolutePath());
|
||||||
if (url != null) cv.put("url", url);
|
if (url != null) cv.put("url", url);
|
||||||
if (db.update("monocles.cids", cv, "cid=?", new String[]{cid.toString()}) < 1) {
|
if (db.update("cids", cv, "cid=?", new String[]{cid.toString()}) < 1) {
|
||||||
db.insertWithOnConflict("monocles.cids", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
db.insertWithOnConflict("cids", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,12 +838,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put("cid", cid.toString());
|
cv.put("cid", cid.toString());
|
||||||
db.insertWithOnConflict("monocles.blocked_media", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
db.insertWithOnConflict("blocked_media", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBlockedMedia(Cid cid) {
|
public boolean isBlockedMedia(Cid cid) {
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
Cursor cursor = db.query("monocles.blocked_media", new String[]{"count(*)"}, "cid=?", new String[]{cid.toString()}, null, null, null);
|
Cursor cursor = db.query("blocked_media", new String[]{"count(*)"}, "cid=?", new String[]{cid.toString()}, null, null, null);
|
||||||
boolean is = false;
|
boolean is = false;
|
||||||
if (cursor.moveToNext()) {
|
if (cursor.moveToNext()) {
|
||||||
is = cursor.getInt(0) > 0;
|
is = cursor.getInt(0) > 0;
|
||||||
|
@ -917,13 +854,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public void clearBlockedMedia() {
|
public void clearBlockedMedia() {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
db.execSQL("DELETE FROM monocles.blocked_media");
|
db.execSQL("DELETE FROM blocked_media");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Multimap<String, String> loadMutedMucUsers() {
|
public Multimap<String, String> loadMutedMucUsers() {
|
||||||
Multimap<String, String> result = HashMultimap.create();
|
Multimap<String, String> result = HashMultimap.create();
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
Cursor cursor = db.query("monocles.muted_participants", new String[]{"muc_jid", "occupant_id"}, null, null, null, null, null);
|
Cursor cursor = db.query("muted_participants", new String[]{"muc_jid", "occupant_id"}, null, null, null, null, null);
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
result.put(cursor.getString(0), cursor.getString(1));
|
result.put(cursor.getString(0), cursor.getString(1));
|
||||||
}
|
}
|
||||||
|
@ -938,7 +875,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put("muc_jid", user.getMuc().toString());
|
cv.put("muc_jid", user.getMuc().toString());
|
||||||
cv.put("occupant_id", user.getOccupantId());
|
cv.put("occupant_id", user.getOccupantId());
|
||||||
db.insertWithOnConflict("monocles.muted_participants", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
db.insertWithOnConflict("muted_participants", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -949,14 +886,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
String where = "muc_jid=? AND occupant_id=?";
|
String where = "muc_jid=? AND occupant_id=?";
|
||||||
String[] whereArgs = {user.getMuc().toString(), user.getOccupantId()};
|
String[] whereArgs = {user.getMuc().toString(), user.getOccupantId()};
|
||||||
db.delete("monocles.muted_participants", where, whereArgs);
|
db.delete("muted_participants", where, whereArgs);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertWebxdcUpdate(final WebxdcUpdate update) {
|
public void insertWebxdcUpdate(final WebxdcUpdate update) {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
db.insertWithOnConflict("monocles.webxdc_updates", null, update.getContentValues(), SQLiteDatabase.CONFLICT_IGNORE);
|
db.insertWithOnConflict("webxdc_updates", null, update.getContentValues(), SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebxdcUpdate findLastWebxdcUpdate(Message message) {
|
public WebxdcUpdate findLastWebxdcUpdate(Message message) {
|
||||||
|
@ -967,7 +904,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
|
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
String[] selectionArgs = {message.getConversation().getUuid(), message.getThread().getContent()};
|
String[] selectionArgs = {message.getConversation().getUuid(), message.getThread().getContent()};
|
||||||
Cursor cursor = db.query("monocles.webxdc_updates", null,
|
Cursor cursor = db.query("webxdc_updates", null,
|
||||||
Message.CONVERSATION + "=? AND thread=?",
|
Message.CONVERSATION + "=? AND thread=?",
|
||||||
selectionArgs, null, null, "serial ASC");
|
selectionArgs, null, null, "serial ASC");
|
||||||
WebxdcUpdate update = null;
|
WebxdcUpdate update = null;
|
||||||
|
@ -981,7 +918,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
public List<WebxdcUpdate> findWebxdcUpdates(Message message, long serial) {
|
public List<WebxdcUpdate> findWebxdcUpdates(Message message, long serial) {
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
String[] selectionArgs = {message.getConversation().getUuid(), message.getThread().getContent(), String.valueOf(serial)};
|
String[] selectionArgs = {message.getConversation().getUuid(), message.getThread().getContent(), String.valueOf(serial)};
|
||||||
Cursor cursor = db.query("monocles.webxdc_updates", null,
|
Cursor cursor = db.query("webxdc_updates", null,
|
||||||
Message.CONVERSATION + "=? AND thread=? AND serial>?",
|
Message.CONVERSATION + "=? AND thread=? AND serial>?",
|
||||||
selectionArgs, null, null, "serial ASC");
|
selectionArgs, null, null, "serial ASC");
|
||||||
long maxSerial = 0;
|
long maxSerial = 0;
|
||||||
|
@ -1007,7 +944,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
public void createMessage(Message message) {
|
public void createMessage(Message message) {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
db.insert(Message.TABLENAME, null, message.getContentValues());
|
db.insert(Message.TABLENAME, null, message.getContentValues());
|
||||||
db.insert("monocles." + Message.TABLENAME, null, message.getmonoclesContentValues());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createAccount(Account account) {
|
public void createAccount(Account account) {
|
||||||
|
@ -1113,8 +1049,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
Cursor cursor;
|
Cursor cursor;
|
||||||
cursor = db.rawQuery(
|
cursor = db.rawQuery(
|
||||||
"SELECT * FROM " + Message.TABLENAME + " " +
|
"SELECT * FROM " + Message.TABLENAME + " " +
|
||||||
"LEFT JOIN monocles." + Message.TABLENAME +
|
|
||||||
" USING (" + Message.UUID + ")" +
|
|
||||||
"WHERE " + Message.UUID + "=?",
|
"WHERE " + Message.UUID + "=?",
|
||||||
new String[]{uuid}
|
new String[]{uuid}
|
||||||
);
|
);
|
||||||
|
@ -1149,8 +1083,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
Cursor cursor;
|
Cursor cursor;
|
||||||
cursor = db.rawQuery(
|
cursor = db.rawQuery(
|
||||||
"SELECT * FROM " + Message.TABLENAME + " " +
|
"SELECT * FROM " + Message.TABLENAME + " " +
|
||||||
"LEFT JOIN monocles." + Message.TABLENAME +
|
|
||||||
" USING (" + Message.UUID + ")" +
|
|
||||||
"WHERE " + Message.UUID + " IN (" + TextUtils.join(",", template) + ") OR " + Message.SERVER_MSG_ID + " IN (" + TextUtils.join(",", template) + ") OR " + Message.REMOTE_MSG_ID + " IN (" + TextUtils.join(",", template) + ")",
|
"WHERE " + Message.UUID + " IN (" + TextUtils.join(",", template) + ") OR " + Message.SERVER_MSG_ID + " IN (" + TextUtils.join(",", template) + ") OR " + Message.REMOTE_MSG_ID + " IN (" + TextUtils.join(",", template) + ")",
|
||||||
params.toArray(new String[0])
|
params.toArray(new String[0])
|
||||||
);
|
);
|
||||||
|
@ -1177,9 +1109,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
String[] selectionArgs = {conversation.getUuid()};
|
String[] selectionArgs = {conversation.getUuid()};
|
||||||
cursor = db.rawQuery(
|
cursor = db.rawQuery(
|
||||||
"SELECT * FROM " + Message.TABLENAME + " " +
|
"SELECT * FROM " + Message.TABLENAME + " " +
|
||||||
"LEFT JOIN monocles." + Message.TABLENAME +
|
"WHERE " + Message.UUID + " IN (" +
|
||||||
" USING (" + Message.UUID + ")" +
|
|
||||||
" WHERE " + Message.UUID + " IN (" +
|
|
||||||
"SELECT " + Message.UUID + " FROM " + Message.TABLENAME +
|
"SELECT " + Message.UUID + " FROM " + Message.TABLENAME +
|
||||||
" WHERE " + Message.CONVERSATION + "=? " +
|
" WHERE " + Message.CONVERSATION + "=? " +
|
||||||
"ORDER BY " + Message.TIME_SENT + " DESC " +
|
"ORDER BY " + Message.TIME_SENT + " DESC " +
|
||||||
|
@ -1192,9 +1122,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
Long.toString(timestamp)};
|
Long.toString(timestamp)};
|
||||||
cursor = db.rawQuery(
|
cursor = db.rawQuery(
|
||||||
"SELECT * FROM " + Message.TABLENAME + " " +
|
"SELECT * FROM " + Message.TABLENAME + " " +
|
||||||
"LEFT JOIN monocles." + Message.TABLENAME +
|
"WHERE " + Message.UUID + " IN (" +
|
||||||
" USING (" + Message.UUID + ")" +
|
|
||||||
" WHERE " + Message.UUID + " IN (" +
|
|
||||||
"SELECT " + Message.UUID + " FROM " + Message.TABLENAME +
|
"SELECT " + Message.UUID + " FROM " + Message.TABLENAME +
|
||||||
" WHERE " + Message.CONVERSATION + "=? AND " +
|
" WHERE " + Message.CONVERSATION + "=? AND " +
|
||||||
Message.TIME_SENT + "<? " +
|
Message.TIME_SENT + "<? " +
|
||||||
|
@ -1330,7 +1258,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
final Conversation conversation, final String messageId) {
|
final Conversation conversation, final String messageId) {
|
||||||
final var db = this.getReadableDatabase();
|
final var db = this.getReadableDatabase();
|
||||||
final String sql =
|
final String sql =
|
||||||
"select * from messages LEFT JOIN monocles.messages USING (uuid) where conversationUuid=? and serverMsgId=? LIMIT 1";
|
"select * from messages LEFT JOIN messages USING (uuid) where conversationUuid=? and serverMsgId=? LIMIT 1";
|
||||||
final String[] args = {conversation.getUuid(), messageId};
|
final String[] args = {conversation.getUuid(), messageId};
|
||||||
final Cursor cursor = db.rawQuery(sql, args);
|
final Cursor cursor = db.rawQuery(sql, args);
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
|
@ -1350,7 +1278,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
final Conversation conversation, final String messageId) {
|
final Conversation conversation, final String messageId) {
|
||||||
final var db = this.getReadableDatabase();
|
final var db = this.getReadableDatabase();
|
||||||
final String sql =
|
final String sql =
|
||||||
"select * from messages LEFT JOIN monocles.messages USING (uuid) where conversationUuid=? and (uuid=? OR remoteMsgId=?) LIMIT 1";
|
"select * from messages LEFT JOIN messages USING (uuid) where conversationUuid=? and (uuid=? OR remoteMsgId=?) LIMIT 1";
|
||||||
final String[] args = {conversation.getUuid(), messageId, messageId};
|
final String[] args = {conversation.getUuid(), messageId, messageId};
|
||||||
final Cursor cursor = db.rawQuery(sql, args);
|
final Cursor cursor = db.rawQuery(sql, args);
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
|
@ -1473,15 +1401,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
if (!includeBody) {
|
if (!includeBody) {
|
||||||
contentValues.remove(Message.BODY);
|
contentValues.remove(Message.BODY);
|
||||||
}
|
}
|
||||||
return db.update(Message.TABLENAME, contentValues, Message.UUID + "=?", args) == 1 &&
|
return db.update(Message.TABLENAME, contentValues, Message.UUID + "=?", args) == 1;
|
||||||
db.update("monocles." + Message.TABLENAME, message.getmonoclesContentValues(), Message.UUID + "=?", args) == 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateMessage(Message message, String uuid) {
|
public boolean updateMessage(Message message, String uuid) {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
String[] args = {uuid};
|
String[] args = {uuid};
|
||||||
return db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args) == 1 &&
|
return db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args) == 1;
|
||||||
db.update("monocles." + Message.TABLENAME, message.getmonoclesContentValues(), Message.UUID + "=?", args) == 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1489,7 +1415,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
SQLiteDatabase db = this.getWritableDatabase();
|
SQLiteDatabase db = this.getWritableDatabase();
|
||||||
String[] args = {uuid};
|
String[] args = {uuid};
|
||||||
return db.delete(Message.TABLENAME, Message.UUID + "=?", args) == 1 &&
|
return db.delete(Message.TABLENAME, Message.UUID + "=?", args) == 1 &&
|
||||||
db.delete("monocles." + Message.TABLENAME, Message.UUID + "=?", args) == 1;
|
db.delete("" + Message.TABLENAME, Message.UUID + "=?", args) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readRoster(Roster roster) {
|
public void readRoster(Roster roster) {
|
||||||
|
@ -1531,7 +1457,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
final String[] args = {conversation.getUuid()};
|
final String[] args = {conversation.getUuid()};
|
||||||
int num = db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
|
int num = db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
|
||||||
db.delete("monocles.webxdc_updates", Message.CONVERSATION + "=?", args);
|
db.delete("webxdc_updates", Message.CONVERSATION + "=?", args);
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
Log.d(Config.LOGTAG, "deleted " + num + " messages for " + conversation.getJid().asBareJid() + " in " + (SystemClock.elapsedRealtime() - start) + "ms");
|
Log.d(Config.LOGTAG, "deleted " + num + " messages for " + conversation.getJid().asBareJid() + " in " + (SystemClock.elapsedRealtime() - start) + "ms");
|
||||||
|
@ -1542,7 +1468,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
db.delete(Message.TABLENAME, "timeSent<?", args);
|
db.delete(Message.TABLENAME, "timeSent<?", args);
|
||||||
db.delete("monocles.messages", "timeReceived<?", args);
|
db.delete("messages", "timeReceived<?", args);
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
@ -2142,4 +2068,59 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
return contacts;
|
return contacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<Message> getMessagesIterable(final Conversation conversation) {
|
||||||
|
return () -> new Iterator<Message>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (messageCursor == null) return false;
|
||||||
|
return !messageCursor.isAfterLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
final SQLiteDatabase database = getReadableDatabase();
|
||||||
|
final String[] queryArgs = {conversation.getUuid(), "1"};
|
||||||
|
Cursor messageCursor = null;
|
||||||
|
|
||||||
|
{
|
||||||
|
messageCursor = database.query(Message.TABLENAME, null, Message.CONVERSATION
|
||||||
|
+ "=? and " + Message.DELETED + "<?", queryArgs, null, null, Message.TIME_SENT
|
||||||
|
+ " ASC", null);
|
||||||
|
if (messageCursor != null) {
|
||||||
|
messageCursor.moveToFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message next() {
|
||||||
|
if (messageCursor == null || messageCursor.isAfterLast()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
Message message = null;
|
||||||
|
try {
|
||||||
|
message = Message.fromCursor(messageCursor, conversation);
|
||||||
|
} catch (IOException e) {
|
||||||
|
messageCursor.close();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
messageCursor.moveToNext();
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
if (messageCursor != null) {
|
||||||
|
messageCursor.close();
|
||||||
|
}
|
||||||
|
if (database != null) {
|
||||||
|
database.close();
|
||||||
|
}
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,12 @@ import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -24,6 +26,7 @@ import com.google.common.base.Strings;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.Conversations;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
|
import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
@ -33,10 +36,13 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
|
||||||
import eu.siacs.conversations.persistance.FileBackend;
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.utils.BackupFileHeader;
|
import eu.siacs.conversations.utils.BackupFileHeader;
|
||||||
import eu.siacs.conversations.utils.Compatibility;
|
import eu.siacs.conversations.utils.Compatibility;
|
||||||
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
@ -74,11 +80,18 @@ public class ExportBackupWorker extends Worker {
|
||||||
|
|
||||||
public static final String MIME_TYPE = "application/vnd.conversations.backup";
|
public static final String MIME_TYPE = "application/vnd.conversations.backup";
|
||||||
|
|
||||||
|
private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 19;
|
private static final int NOTIFICATION_ID = 19;
|
||||||
private static final int BACKUP_CREATED_NOTIFICATION_ID = 23;
|
private static final int BACKUP_CREATED_NOTIFICATION_ID = 23;
|
||||||
|
|
||||||
private final boolean recurringBackup;
|
private final boolean recurringBackup;
|
||||||
|
|
||||||
|
boolean ReadableLogsEnabled = false;
|
||||||
|
private DatabaseBackend mDatabaseBackend;
|
||||||
|
private List<Account> mAccounts;
|
||||||
|
|
||||||
|
|
||||||
public ExportBackupWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
public ExportBackupWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||||
super(context, workerParams);
|
super(context, workerParams);
|
||||||
final var inputData = workerParams.getInputData();
|
final var inputData = workerParams.getInputData();
|
||||||
|
@ -234,6 +247,25 @@ public class ExportBackupWorker extends Worker {
|
||||||
Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile());
|
Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile());
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mDatabaseBackend = DatabaseBackend.getInstance(Conversations.getContext());
|
||||||
|
mAccounts = mDatabaseBackend.getAccounts();
|
||||||
|
final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
|
ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", getApplicationContext().getResources().getBoolean(R.bool.plain_text_logs));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (ReadableLogsEnabled) { // todo
|
||||||
|
List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE);
|
||||||
|
conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED));
|
||||||
|
for (Conversation conversation : conversations) {
|
||||||
|
writeToFile(conversation);
|
||||||
|
Log.d(Config.LOGTAG, "Exporting readable logs for " + conversation.getJid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +277,7 @@ public class ExportBackupWorker extends Worker {
|
||||||
|
|
||||||
private void messageExportmonocles(final SQLiteDatabase db, final String uuid, final JsonWriter writer, final Progress progress) throws IOException {
|
private void messageExportmonocles(final SQLiteDatabase db, final String uuid, final JsonWriter writer, final Progress progress) throws IOException {
|
||||||
final var notificationManager = getApplicationContext().getSystemService(NotificationManager.class);
|
final var notificationManager = getApplicationContext().getSystemService(NotificationManager.class);
|
||||||
Cursor cursor = db.rawQuery("select mmessages.* from messages join monocles.messages mmessages using (uuid) join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=?", new String[]{uuid});
|
Cursor cursor = db.rawQuery("select mmessages.* from messages join messages mmessages using (uuid) join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=?", new String[]{uuid});
|
||||||
int size = cursor != null ? cursor.getCount() : 0;
|
int size = cursor != null ? cursor.getCount() : 0;
|
||||||
Log.d(Config.LOGTAG, "exporting " + size + " monocles messages for account " + uuid);
|
Log.d(Config.LOGTAG, "exporting " + size + " monocles messages for account " + uuid);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -253,7 +285,7 @@ public class ExportBackupWorker extends Worker {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
writer.name("table");
|
writer.name("table");
|
||||||
writer.value("monocles." + Message.TABLENAME);
|
writer.value("" + Message.TABLENAME);
|
||||||
writer.name("values");
|
writer.name("values");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
for (int j = 0; j < cursor.getColumnCount(); ++j) {
|
for (int j = 0; j < cursor.getColumnCount(); ++j) {
|
||||||
|
@ -275,13 +307,13 @@ public class ExportBackupWorker extends Worker {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor = db.rawQuery("select webxdc_updates.* from " + Conversation.TABLENAME + " join monocles.webxdc_updates webxdc_updates on " + Conversation.TABLENAME + ".uuid=webxdc_updates." + Message.CONVERSATION + " where conversations.accountUuid=?", new String[]{uuid});
|
cursor = db.rawQuery("select webxdc_updates.* from " + Conversation.TABLENAME + " join webxdc_updates webxdc_updates on " + Conversation.TABLENAME + ".uuid=webxdc_updates." + Message.CONVERSATION + " where conversations.accountUuid=?", new String[]{uuid});
|
||||||
size = cursor != null ? cursor.getCount() : 0;
|
size = cursor != null ? cursor.getCount() : 0;
|
||||||
Log.d(Config.LOGTAG, "exporting " + size + " WebXDC updates for account " + uuid);
|
Log.d(Config.LOGTAG, "exporting " + size + " WebXDC updates for account " + uuid);
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
writer.name("table");
|
writer.name("table");
|
||||||
writer.value("monocles.webxdc_updates");
|
writer.value("webxdc_updates");
|
||||||
writer.name("values");
|
writer.name("values");
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
for (int j = 0; j < cursor.getColumnCount(); ++j) {
|
for (int j = 0; j < cursor.getColumnCount(); ++j) {
|
||||||
|
@ -537,4 +569,70 @@ public class ExportBackupWorker extends Worker {
|
||||||
return notification.build();
|
return notification.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeToFile(Conversation conversation) {
|
||||||
|
Jid accountJid = resolveAccountUuid(conversation.getAccountUuid());
|
||||||
|
Jid contactJid = conversation.getJid();
|
||||||
|
final File dir = new File(FileBackend.getBackupDirectory(getApplicationContext()), accountJid.asBareJid().toString());
|
||||||
|
dir.mkdirs();
|
||||||
|
|
||||||
|
BufferedWriter bw = null;
|
||||||
|
try {
|
||||||
|
for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) {
|
||||||
|
if (message == null)
|
||||||
|
continue;
|
||||||
|
if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) {
|
||||||
|
String date = DATE_FORMAT.format(new Date(message.getTimeSent()));
|
||||||
|
if (bw == null) {
|
||||||
|
bw = new BufferedWriter(new FileWriter(
|
||||||
|
new File(dir, contactJid.asBareJid().toString() + ".txt")));
|
||||||
|
}
|
||||||
|
String jid = null;
|
||||||
|
switch (message.getStatus()) {
|
||||||
|
case Message.STATUS_RECEIVED:
|
||||||
|
jid = getMessageCounterpart(message);
|
||||||
|
break;
|
||||||
|
case Message.STATUS_SEND:
|
||||||
|
case Message.STATUS_SEND_RECEIVED:
|
||||||
|
case Message.STATUS_SEND_DISPLAYED:
|
||||||
|
case Message.STATUS_SEND_FAILED:
|
||||||
|
jid = accountJid.asBareJid().toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (jid != null) {
|
||||||
|
String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody();
|
||||||
|
bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid, body.replace("\\\n", "\\ \n").replace("\n", "\\ \n")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (bw != null) {
|
||||||
|
bw.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Jid resolveAccountUuid(String accountUuid) {
|
||||||
|
for (Account account : mAccounts) {
|
||||||
|
if (account.getUuid().equals(accountUuid)) {
|
||||||
|
return account.getJid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMessageCounterpart(Message message) {
|
||||||
|
String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART);
|
||||||
|
if (trueCounterpart != null) {
|
||||||
|
return trueCounterpart;
|
||||||
|
} else {
|
||||||
|
return message.getCounterpart().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,5 @@
|
||||||
<bool name="secure_tls">false</bool>
|
<bool name="secure_tls">false</bool>
|
||||||
<bool name="prefer_ipv6">false</bool>
|
<bool name="prefer_ipv6">false</bool>
|
||||||
<bool name="use_colored_muc_names">false</bool>
|
<bool name="use_colored_muc_names">false</bool>
|
||||||
|
<bool name="plain_text_logs">false</bool>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1412,4 +1412,6 @@
|
||||||
<string name="show_to_contacts_only">Show to contacts only</string>
|
<string name="show_to_contacts_only">Show to contacts only</string>
|
||||||
<string name="success_export_settings">Settings successfully exported</string>
|
<string name="success_export_settings">Settings successfully exported</string>
|
||||||
<string name="error_export_settings">Error while exporting settings</string>
|
<string name="error_export_settings">Error while exporting settings</string>
|
||||||
|
<string name="pref_export_plain_text_logs">Export plain text</string>
|
||||||
|
<string name="pref_export_plain_text_logs_summary">Export messages unencrypted in human readable plain text</string>
|
||||||
</resources>
|
</resources>
|
|
@ -27,6 +27,12 @@
|
||||||
android:key="backup_directory"
|
android:key="backup_directory"
|
||||||
android:summary="@string/pref_create_backup_summary" />
|
android:summary="@string/pref_create_backup_summary" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="@bool/plain_text_logs"
|
||||||
|
android:key="export_plain_text_logs"
|
||||||
|
android:summary="@string/pref_export_plain_text_logs_summary"
|
||||||
|
android:title="@string/pref_export_plain_text_logs" />
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_category_settings">
|
android:title="@string/pref_category_settings">
|
||||||
<Preference
|
<Preference
|
||||||
|
|
|
@ -95,9 +95,9 @@ public class ImportBackupService extends Service {
|
||||||
Account.TABLENAME,
|
Account.TABLENAME,
|
||||||
Conversation.TABLENAME,
|
Conversation.TABLENAME,
|
||||||
Message.TABLENAME,
|
Message.TABLENAME,
|
||||||
"monocles." + Message.TABLENAME,
|
Message.TABLENAME,
|
||||||
"monocles.webxdc_updates",
|
"webxdc_updates",
|
||||||
"monocles.muted_participants",
|
"muted_participants",
|
||||||
SQLiteAxolotlStore.PREKEY_TABLENAME,
|
SQLiteAxolotlStore.PREKEY_TABLENAME,
|
||||||
SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
|
SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
|
||||||
SQLiteAxolotlStore.SESSION_TABLENAME,
|
SQLiteAxolotlStore.SESSION_TABLENAME,
|
||||||
|
|
Loading…
Reference in a new issue