From 6b79a078b2a5bae55191ce7acd65e9d43a1982f8 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Tue, 1 Mar 2022 20:29:37 -0500 Subject: [PATCH] Store and display message subject Creates a new database for cheogram that is attached to the main connection so that joins between the two can happen. This allows us to migrate our DB schema seperately from the Conversations schema to keep merging with upstream more realistic. --- .../entities/IndividualMessage.java | 2 +- .../siacs/conversations/entities/Message.java | 25 +++++++- .../conversations/parser/MessageParser.java | 1 + .../persistance/DatabaseBackend.java | 58 ++++++++++++++++--- .../ui/adapter/MessageAdapter.java | 12 ++++ src/main/res/layout/message_received.xml | 14 ++++- src/main/res/layout/message_sent.xml | 14 ++++- 7 files changed, 114 insertions(+), 12 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java b/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java index 8f416a301aae5799ca52ab585e6f3092d48ce37b..0d71d5782667e03866c1f035a0cbcf892a2656a9 100644 --- a/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java +++ b/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java @@ -44,7 +44,7 @@ public class IndividualMessage extends Message { } private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, String edited, boolean oob, String errorMessage, Set readByMarkers, boolean markable, boolean deleted, String bodyLanguage) { - super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable, deleted, bodyLanguage); + super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable, deleted, bodyLanguage, null); } @Override diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index aa197aa4449c3ca0216c5742d8baaab8af6e5da5..87f43a3e4161d9753be5ae5e5c1464d3242cf517 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -93,6 +93,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable protected Jid counterpart; protected Jid trueCounterpart; protected String body; + protected String subject; protected String encryptedBody; protected long timeSent; protected int encryption; @@ -152,6 +153,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable null, false, false, + null, null); } @@ -177,6 +179,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable null, false, false, + null, null); } @@ -186,7 +189,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable final String remoteMsgId, final String relativeFilePath, final String serverMsgId, final String fingerprint, final boolean read, final String edited, final boolean oob, final String errorMessage, final Set readByMarkers, - final boolean markable, final boolean deleted, final String bodyLanguage) { + final boolean markable, final boolean deleted, final String bodyLanguage, final String subject) { this.conversation = conversation; this.uuid = uuid; this.conversationUuid = conversationUUid; @@ -210,6 +213,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable this.markable = markable; this.deleted = deleted; this.bodyLanguage = bodyLanguage; + this.subject = subject; } public static Message fromCursor(Cursor cursor, Conversation conversation) { @@ -235,7 +239,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndex(READ_BY_MARKERS))), cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0, cursor.getInt(cursor.getColumnIndex(DELETED)) > 0, - cursor.getString(cursor.getColumnIndex(BODY_LANGUAGE)) + cursor.getString(cursor.getColumnIndex(BODY_LANGUAGE)), + cursor.getString(cursor.getColumnIndex("subject")) ); } @@ -265,6 +270,13 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable return message; } + public ContentValues getCheogramContentValues() { + ContentValues values = new ContentValues(); + values.put(UUID, uuid); + values.put("subject", subject); + return values; + } + @Override public ContentValues getContentValues() { ContentValues values = new ContentValues(); @@ -349,6 +361,14 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable this.fileParams = null; } + public String getSubject() { + return subject; + } + + public synchronized void setSubject(String subject) { + this.subject = subject; + } + public void setMucUser(MucOptions.User user) { this.user = new WeakReference<>(user); } @@ -632,6 +652,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED && this.getType() == message.getType() && + this.getSubject() != null && //this.getStatus() == message.getStatus() && isStatusMergeable(this.getStatus(), message.getStatus()) && this.getEncryption() == message.getEncryption() && diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index f45b1e89b871932ffee9e2bcf3838da03ea3f200..c96a2ff65c449929a6789146128049abd79bb15f 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -571,6 +571,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } + message.setSubject(original.findChildContent("subject")); message.setCounterpart(counterpart); message.setRemoteMsgId(remoteMsgId); message.setServerMsgId(serverMsgId); diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index de9bc0d2ce32e5135bdb7613ea8e24bd4c89b85d..08bffb9da09296d1073ce27a4a5912e63ac2568c 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -180,8 +180,11 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static final String CREATE_MESSAGE_DELETE_TRIGGER = "CREATE TRIGGER after_message_delete AFTER DELETE ON " + Message.TABLENAME + " BEGIN DELETE FROM messages_index WHERE rowid=OLD.rowid; END;"; private static final String COPY_PREEXISTING_ENTRIES = "INSERT INTO messages_index(messages_index) VALUES('rebuild');"; + protected Context context; + private DatabaseBackend(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); + this.context = context; } private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) { @@ -209,10 +212,38 @@ public class DatabaseBackend extends SQLiteOpenHelper { return instance; } + protected void cheogramMigrate(SQLiteDatabase db) { + db.beginTransaction(); + + try { + Cursor cursor = db.rawQuery("PRAGMA cheogram.user_version", null); + cursor.moveToNext(); + int cheogramVersion = cursor.getInt(0); + cursor.close(); + + if(cheogramVersion < 1) { + // No cross-DB foreign keys unfortunately + db.execSQL( + "CREATE TABLE cheogram." + Message.TABLENAME + "(" + + Message.UUID + " TEXT PRIMARY KEY, " + + "subject TEXT" + + ")" + ); + db.execSQL("PRAGMA cheogram.user_version = 1"); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + @Override public void onConfigure(SQLiteDatabase db) { db.execSQL("PRAGMA foreign_keys=ON"); db.rawQuery("PRAGMA secure_delete=ON", null).close(); + db.execSQL("ATTACH DATABASE ? AS cheogram", new Object[]{context.getDatabasePath("cheogram").getPath()}); + cheogramMigrate(db); } @Override @@ -678,6 +709,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void createMessage(Message message) { SQLiteDatabase db = this.getWritableDatabase(); db.insert(Message.TABLENAME, null, message.getContentValues()); + db.insert("cheogram." + Message.TABLENAME, null, message.getCheogramContentValues()); } public void createAccount(Account account) { @@ -787,16 +819,28 @@ public class DatabaseBackend extends SQLiteOpenHelper { Cursor cursor; if (timestamp == -1) { String[] selectionArgs = {conversation.getUuid()}; - cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION - + "=?", selectionArgs, null, null, Message.TIME_SENT - + " DESC", String.valueOf(limit)); + cursor = db.rawQuery( + "SELECT * FROM " + Message.TABLENAME + " " + + "LEFT JOIN cheogram." + Message.TABLENAME + + " USING (" + Message.UUID + ")" + + "WHERE " + Message.CONVERSATION + "=? " + + "ORDER BY " + Message.TIME_SENT + " DESC " + + "LIMIT " + String.valueOf(limit), + selectionArgs + ); } else { String[] selectionArgs = {conversation.getUuid(), Long.toString(timestamp)}; - cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION - + "=? and " + Message.TIME_SENT + " { viewHolder.image = view.findViewById(R.id.message_image); viewHolder.messageBody = view.findViewById(R.id.message_body); viewHolder.time = view.findViewById(R.id.message_time); + viewHolder.subject = view.findViewById(R.id.message_subject); viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); viewHolder.audioPlayer = view.findViewById(R.id.audio_player); break; @@ -668,6 +669,7 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.image = view.findViewById(R.id.message_image); viewHolder.messageBody = view.findViewById(R.id.message_body); viewHolder.time = view.findViewById(R.id.message_time); + viewHolder.subject = view.findViewById(R.id.message_subject); viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); viewHolder.encryption = view.findViewById(R.id.message_encryption); viewHolder.audioPlayer = view.findViewById(R.id.audio_player); @@ -860,6 +862,15 @@ public class MessageAdapter extends ArrayAdapter { } } + if (type == RECEIVED || type == SENT) { + if (message.getSubject() == null) { + viewHolder.subject.setVisibility(View.GONE); + } else { + viewHolder.subject.setVisibility(View.VISIBLE); + viewHolder.subject.setText(message.getSubject()); + } + } + displayStatus(viewHolder, message, type, darkBackground); return view; @@ -934,6 +945,7 @@ public class MessageAdapter extends ArrayAdapter { protected ImageView indicator; protected ImageView indicatorReceived; protected TextView time; + protected TextView subject; protected TextView messageBody; protected ImageView contact_picture; protected TextView status_message; diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml index 75b1eb340b1d7e92fdf3bb0aed7860a11fa9e1a0..795af12d803b570fec7812c2e9b60fa51ad77ba7 100644 --- a/src/main/res/layout/message_received.xml +++ b/src/main/res/layout/message_received.xml @@ -48,6 +48,18 @@ android:orientation="horizontal" android:paddingBottom="2dp"> + + - \ No newline at end of file + diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml index f84d07e3426699062d52bf434a7a3c31e7327208..1e669efe1820371cd43dbe3ae0a87b68d91fab68 100644 --- a/src/main/res/layout/message_sent.xml +++ b/src/main/res/layout/message_sent.xml @@ -62,6 +62,18 @@ android:orientation="horizontal" android:paddingBottom="2dp"> + + - \ No newline at end of file +