diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt index 244f5db117..4dec345fa8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -1423,27 +1423,67 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa const val QUOTE_MISSING: String = "quote_missing" const val SHARED_CONTACTS: String = "shared_contacts" const val LINK_PREVIEWS: String = "previews" + + + private const val IS_DELETED_COLUMN_DEF = """ + $IS_DELETED GENERATED ALWAYS AS ( + ($MESSAGE_BOX & ${MmsSmsColumns.Types.BASE_TYPE_MASK}) IN (${MmsSmsColumns.Types.BASE_DELETED_OUTGOING_TYPE}, ${MmsSmsColumns.Types.BASE_DELETED_INCOMING_TYPE}) + ) VIRTUAL + """ + const val CREATE_TABLE: String = - "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + - THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " + - READ + " INTEGER DEFAULT 0, " + "m_id" + " TEXT, " + "sub" + " TEXT, " + - "sub_cs" + " INTEGER, " + BODY + " TEXT, " + PART_COUNT + " INTEGER, " + - "ct_t" + " TEXT, " + CONTENT_LOCATION + " TEXT, " + ADDRESS + " TEXT, " + - ADDRESS_DEVICE_ID + " INTEGER, " + - EXPIRY + " INTEGER, " + "m_cls" + " TEXT, " + MESSAGE_TYPE + " INTEGER, " + - "v" + " INTEGER, " + MESSAGE_SIZE + " INTEGER, " + "pri" + " INTEGER, " + - "rr" + " INTEGER, " + "rpt_a" + " INTEGER, " + "resp_st" + " INTEGER, " + - STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + "retr_st" + " INTEGER, " + - "retr_txt" + " TEXT, " + "retr_txt_cs" + " INTEGER, " + "read_status" + " INTEGER, " + - "ct_cls" + " INTEGER, " + "resp_txt" + " TEXT, " + "d_tm" + " INTEGER, " + - DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + - NETWORK_FAILURE + " TEXT DEFAULT NULL," + "d_rpt" + " INTEGER, " + - SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + - EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " + - QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " + - QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0, " + - LINK_PREVIEWS + " TEXT);" + """CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY AUTOINCREMENT, + $THREAD_ID INTEGER, + $DATE_SENT INTEGER, + $DATE_RECEIVED INTEGER, + $MESSAGE_BOX INTEGER, + $READ INTEGER DEFAULT 0, + m_id TEXT, + sub TEXT, + sub_cs INTEGER, + $BODY TEXT, + $PART_COUNT INTEGER, + ct_t TEXT, + $CONTENT_LOCATION TEXT, + $ADDRESS TEXT, + $ADDRESS_DEVICE_ID INTEGER, + $EXPIRY INTEGER, + m_cls TEXT, + $MESSAGE_TYPE INTEGER, + v INTEGER, + $MESSAGE_SIZE INTEGER, + pri INTEGER, + rr INTEGER, + rpt_a INTEGER, + resp_st INTEGER, + $STATUS INTEGER, + $TRANSACTION_ID TEXT, + retr_st INTEGER, + retr_txt TEXT, + retr_txt_cs INTEGER, + read_status INTEGER, + ct_cls INTEGER, + resp_txt TEXT, + d_tm INTEGER, + $DELIVERY_RECEIPT_COUNT INTEGER DEFAULT 0, + $MISMATCHED_IDENTITIES TEXT DEFAULT NULL, + $NETWORK_FAILURE TEXT DEFAULT NULL, + d_rpt INTEGER, + $SUBSCRIPTION_ID INTEGER DEFAULT -1, + $EXPIRES_IN INTEGER DEFAULT 0, + $EXPIRE_STARTED INTEGER DEFAULT 0, + $NOTIFIED INTEGER DEFAULT 0, + $READ_RECEIPT_COUNT INTEGER DEFAULT 0, + $QUOTE_ID INTEGER DEFAULT 0, + $QUOTE_AUTHOR TEXT, + $QUOTE_BODY TEXT, + $QUOTE_ATTACHMENT INTEGER DEFAULT -1, + $QUOTE_MISSING INTEGER DEFAULT 0, + $SHARED_CONTACTS TEXT, + $UNIDENTIFIED INTEGER DEFAULT 0, + $LINK_PREVIEWS TEXT, + $IS_DELETED_COLUMN_DEF);""" @JvmField val CREATE_INDEXS: Array = arrayOf( @@ -1454,6 +1494,9 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa "CREATE INDEX IF NOT EXISTS mms_date_sent_index ON $TABLE_NAME ($DATE_SENT);", "CREATE INDEX IF NOT EXISTS mms_thread_date_index ON $TABLE_NAME ($THREAD_ID, $DATE_RECEIVED);" ) + + const val ADD_IS_DELETED_COLUMN: String = "ALTER TABLE $TABLE_NAME ADD COLUMN $IS_DELETED_COLUMN_DEF" + private val MMS_PROJECTION: Array = arrayOf( "$TABLE_NAME.$ID AS $ID", THREAD_ID, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java index a5db6ca4d3..52111c4242 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -29,6 +29,7 @@ public interface MmsSmsColumns { public static final String REACTIONS_LAST_SEEN = "reactions_last_seen"; public static final String HAS_MENTION = "has_mention"; + public static final String IS_DELETED = "is_deleted"; public static class Types { protected static final long TOTAL_MASK = 0xFFFFFFFF; @@ -185,6 +186,9 @@ public interface MmsSmsColumns { } public static boolean isDeletedMessage(long type) { + // Note: if you change the logic below, you must also change the is_deleted database column (by doing migration), + // which must have the same logic as here. See [MmsDatabase.IS_DELETED_COLUMN_DEF] or [SmsDatabase.IS_DELETED_COLUMN_DEF]. + // Failing to do so will result in inconsistencies in the UI. return (type & BASE_TYPE_MASK) == BASE_DELETED_OUTGOING_TYPE || (type & BASE_TYPE_MASK) == BASE_DELETED_INCOMING_TYPE; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 510272cfb8..313ee2a81a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -336,7 +336,7 @@ public class MmsSmsDatabase extends Database { String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC"; // make sure the last message isn't marked as deleted String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + - "(ifnull("+SmsDatabase.TYPE+", "+MmsDatabase.MESSAGE_BOX+") & "+BASE_TYPE_MASK+") NOT IN ("+BASE_DELETED_OUTGOING_TYPE+", "+BASE_DELETED_INCOMING_TYPE+")"; // this ugly line checks whether the type is deleted (incoming or outgoing) for either the sms table or the mms table + "NOT " + MmsSmsColumns.IS_DELETED; try (Cursor cursor = queryTables(PROJECTION, selection, order, "1")) { if (cursor.moveToFirst()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java index 106cc86e17..9876e8de7e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SearchDatabase.java @@ -69,7 +69,7 @@ public class SearchDatabase extends Database { "FROM " + SmsDatabase.TABLE_NAME + " " + "INNER JOIN " + SMS_FTS_TABLE_NAME + " ON " + SMS_FTS_TABLE_NAME + "." + ID + " = " + SmsDatabase.TABLE_NAME + "." + SmsDatabase.ID + " " + "INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + SMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " " + - "WHERE " + SMS_FTS_TABLE_NAME + " MATCH ? " + + "WHERE " + SMS_FTS_TABLE_NAME + " MATCH ? " + " AND NOT " + MmsSmsColumns.IS_DELETED + " " + "UNION ALL " + "SELECT " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " + @@ -80,7 +80,7 @@ public class SearchDatabase extends Database { "FROM " + MmsDatabase.TABLE_NAME + " " + "INNER JOIN " + MMS_FTS_TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " " + "INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " " + - "WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? " + + "WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? " + " AND NOT " + MmsSmsColumns.IS_DELETED + " " + "ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC " + "LIMIT ?"; @@ -94,7 +94,7 @@ public class SearchDatabase extends Database { "FROM " + SmsDatabase.TABLE_NAME + " " + "INNER JOIN " + SMS_FTS_TABLE_NAME + " ON " + SMS_FTS_TABLE_NAME + "." + ID + " = " + SmsDatabase.TABLE_NAME + "." + SmsDatabase.ID + " " + "INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + SMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " " + - "WHERE " + SMS_FTS_TABLE_NAME + " MATCH ? AND " + SmsDatabase.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " + + "WHERE " + SMS_FTS_TABLE_NAME + " MATCH ? AND " + SmsDatabase.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " + " AND NOT " + MmsSmsColumns.IS_DELETED + " " + "UNION ALL " + "SELECT " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " + @@ -105,7 +105,7 @@ public class SearchDatabase extends Database { "FROM " + MmsDatabase.TABLE_NAME + " " + "INNER JOIN " + MMS_FTS_TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " " + "INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " " + - "WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? AND " + MmsDatabase.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " + + "WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? AND " + MmsDatabase.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " + " AND NOT " + MmsSmsColumns.IS_DELETED + " " + "ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC " + "LIMIT 500"; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 6deca0c939..7688a463c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -78,6 +78,9 @@ public class SmsDatabase extends MessagingDatabase { public static final String SUBJECT = "subject"; public static final String SERVICE_CENTER = "service_center"; + private static final String IS_DELETED_COLUMN_DEF = IS_DELETED + " GENERATED ALWAYS AS ((" + TYPE + + " & " + Types.BASE_TYPE_MASK + ") IN (" + Types.BASE_DELETED_OUTGOING_TYPE + ", " + Types.BASE_DELETED_INCOMING_TYPE +")) VIRTUAL"; + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " integer PRIMARY KEY, " + THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + @@ -85,7 +88,7 @@ public class SmsDatabase extends MessagingDatabase { DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0, " + - READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNIDENTIFIED + " INTEGER DEFAULT 0);"; + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNIDENTIFIED + " INTEGER DEFAULT 0, " + IS_DELETED_COLUMN_DEF +");"; public static final String[] CREATE_INDEXS = { @@ -137,6 +140,8 @@ public class SmsDatabase extends MessagingDatabase { "DROP TABLE " + TEMP_TABLE_NAME }; + public static final String ADD_IS_DELETED_COLUMN = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + IS_DELETED_COLUMN_DEF; + private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache(); private static final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index b6ebd6db84..5bc8c58ba4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -90,9 +90,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV44 = 65; private static final int lokiV45 = 66; private static final int lokiV46 = 67; + private static final int lokiV47 = 68; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes - private static final int DATABASE_VERSION = lokiV46; + private static final int DATABASE_VERSION = lokiV47; private static final int MIN_DATABASE_VERSION = lokiV7; private static final String CIPHER3_DATABASE_NAME = "signal.db"; public static final String DATABASE_NAME = "signal_v4.db"; @@ -628,6 +629,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiAPIDatabase.CREATE_LAST_LEGACY_MESSAGE_TABLE); } + if (oldVersion < lokiV47) { + db.execSQL(SmsDatabase.ADD_IS_DELETED_COLUMN); + db.execSQL(MmsDatabase.ADD_IS_DELETED_COLUMN); + } + db.setTransactionSuccessful(); } finally { db.endTransaction();