Merge branch 'subject'

Stephen Paul Weber created

* subject:
  Store and display message subject

Change summary

src/main/java/eu/siacs/conversations/entities/IndividualMessage.java  |  2 
src/main/java/eu/siacs/conversations/entities/Message.java            | 25 
src/main/java/eu/siacs/conversations/parser/MessageParser.java        |  1 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java | 58 
src/main/java/eu/siacs/conversations/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(-)

Detailed changes

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<ReadByMarker> 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

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<ReadByMarker> 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() &&

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);

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 + "<?", 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 + "=? AND " +
+                Message.TIME_SENT + "<? " +
+                "ORDER BY " + Message.TIME_SENT + " DESC " +
+                "LIMIT " + String.valueOf(limit),
+                selectionArgs
+            );
         }
         CursorUtils.upgradeCursorWindowSize(cursor);
         while (cursor.moveToNext()) {

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

@@ -655,6 +655,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
                     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<Message> {
                     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<Message> {
             }
         }
 
+        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<Message> {
         protected ImageView indicator;
         protected ImageView indicatorReceived;
         protected TextView time;
+        protected TextView subject;
         protected TextView messageBody;
         protected ImageView contact_picture;
         protected TextView status_message;

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

@@ -48,6 +48,18 @@
                 android:orientation="horizontal"
                 android:paddingBottom="2dp">
 
+                <TextView
+                    android:id="@+id/message_subject"
+                    android:visibility="gone"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical"
+                    android:gravity="center_vertical"
+                    android:layout_marginRight="4sp"
+                    android:accessibilityTraversalAfter="@id/message_photo"
+                    android:accessibilityTraversalBefore="@id/message_time"
+                    android:textAppearance="@style/TextAppearance.Conversations.Caption.OnDark.Bold"/>
+
                 <TextView
                     android:id="@+id/message_encryption"
                     android:layout_width="wrap_content"
@@ -91,4 +103,4 @@
         </LinearLayout>
     </LinearLayout>
 
-</RelativeLayout>
+</RelativeLayout>

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

@@ -62,6 +62,18 @@
                 android:orientation="horizontal"
                 android:paddingBottom="2dp">
 
+                <TextView
+                    android:id="@+id/message_subject"
+                    android:visibility="gone"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical"
+                    android:gravity="center_vertical"
+                    android:layout_marginRight="4sp"
+                    android:accessibilityTraversalAfter="@id/message_photo"
+                    android:accessibilityTraversalBefore="@id/message_time"
+                    android:textAppearance="@style/TextAppearance.Conversations.Caption.OnDark.Bold"/>
+
                 <TextView
                     android:id="@+id/message_time"
                     android:layout_width="wrap_content"
@@ -105,4 +117,4 @@
         </LinearLayout>
     </LinearLayout>
 
-</RelativeLayout>
+</RelativeLayout>