Save receipt timestamps on sms/mms database.
This commit is contained in:
parent
3dc1ce3353
commit
0273d0f285
5 changed files with 74 additions and 35 deletions
|
@ -13,33 +13,53 @@ public class EarlyReceiptCache {
|
|||
|
||||
private static final String TAG = Log.tag(EarlyReceiptCache.class);
|
||||
|
||||
private final LRUCache<Long, Map<RecipientId, Long>> cache = new LRUCache<>(100);
|
||||
private final LRUCache<Long, Map<RecipientId, Receipt>> cache = new LRUCache<>(100);
|
||||
private final String name;
|
||||
|
||||
public EarlyReceiptCache(@NonNull String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public synchronized void increment(long timestamp, @NonNull RecipientId origin) {
|
||||
Map<RecipientId, Long> receipts = cache.get(timestamp);
|
||||
public synchronized void increment(long timestamp, @NonNull RecipientId origin, long receiptTimestamp) {
|
||||
Map<RecipientId, Receipt> receipts = cache.get(timestamp);
|
||||
|
||||
if (receipts == null) {
|
||||
receipts = new HashMap<>();
|
||||
}
|
||||
|
||||
Long count = receipts.get(origin);
|
||||
Receipt receipt = receipts.get(origin);
|
||||
|
||||
if (count != null) {
|
||||
receipts.put(origin, ++count);
|
||||
if (receipt != null) {
|
||||
receipt.count++;
|
||||
receipt.timestamp = receiptTimestamp;
|
||||
} else {
|
||||
receipts.put(origin, 1L);
|
||||
receipt = new Receipt(1, receiptTimestamp);
|
||||
}
|
||||
receipts.put(origin, receipt);
|
||||
|
||||
cache.put(timestamp, receipts);
|
||||
}
|
||||
|
||||
public synchronized Map<RecipientId, Long> remove(long timestamp) {
|
||||
Map<RecipientId, Long> receipts = cache.remove(timestamp);
|
||||
public synchronized Map<RecipientId, Receipt> remove(long timestamp) {
|
||||
Map<RecipientId, Receipt> receipts = cache.remove(timestamp);
|
||||
return receipts != null ? receipts : new HashMap<>();
|
||||
}
|
||||
|
||||
public class Receipt {
|
||||
private long count;
|
||||
private long timestamp;
|
||||
|
||||
private Receipt(long count, long timestamp) {
|
||||
this.count = count;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,7 +170,8 @@ public class MmsDatabase extends MessageDatabase {
|
|||
MENTIONS_SELF + " INTEGER DEFAULT 0, " +
|
||||
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0, " +
|
||||
SERVER_GUID + " TEXT DEFAULT NULL);";
|
||||
SERVER_GUID + " TEXT DEFAULT NULL, "+
|
||||
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS mms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
|
||||
|
@ -617,25 +618,29 @@ public class MmsDatabase extends MessageDatabase {
|
|||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
Set<ThreadUpdate> threadUpdates = new HashSet<>();
|
||||
|
||||
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, receiptType.getColumnName()},
|
||||
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, receiptType.getColumnName(), RECEIPT_TIMESTAMP},
|
||||
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
|
||||
null, null, null, null))
|
||||
{
|
||||
while (cursor.moveToNext()) {
|
||||
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
|
||||
RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
|
||||
if (Types.isOutgoingMessageType(CursorUtil.requireLong(cursor, MESSAGE_BOX))) {
|
||||
RecipientId theirRecipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
|
||||
RecipientId ourRecipientId = messageId.getRecipientId();
|
||||
String columnName = receiptType.getColumnName();
|
||||
|
||||
if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
|
||||
long id = CursorUtil.requireLong(cursor, ID);
|
||||
long threadId = CursorUtil.requireLong(cursor, THREAD_ID);
|
||||
int status = receiptType.getGroupStatus();
|
||||
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0;
|
||||
boolean isFirstIncrement = CursorUtil.requireLong(cursor, columnName) == 0;
|
||||
long savedTimestamp = CursorUtil.requireLong(cursor, RECEIPT_TIMESTAMP);
|
||||
long updatedTimestamp = isFirstIncrement ? Math.max(savedTimestamp, timestamp) : savedTimestamp;
|
||||
|
||||
database.execSQL("UPDATE " + TABLE_NAME + " SET " +
|
||||
columnName + " = " + columnName + " + 1 WHERE " + ID + " = ?",
|
||||
new String[] {String.valueOf(id)});
|
||||
columnName + " = " + columnName + " + 1, " +
|
||||
RECEIPT_TIMESTAMP + " = ? WHERE " +
|
||||
ID + " = ?",
|
||||
SqlUtil.buildArgs(updatedTimestamp, id));
|
||||
|
||||
DatabaseFactory.getGroupReceiptDatabase(context).update(ourRecipientId, id, status, timestamp);
|
||||
|
||||
|
@ -645,7 +650,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
}
|
||||
|
||||
if (threadUpdates.size() > 0 && receiptType == ReceiptType.DELIVERY) {
|
||||
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
|
||||
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId(), timestamp);
|
||||
}
|
||||
|
||||
return threadUpdates;
|
||||
|
@ -1484,7 +1489,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
|
||||
}
|
||||
|
||||
Map<RecipientId, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
|
||||
Map<RecipientId, EarlyReceiptCache.Receipt> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(DATE_SENT, message.getSentTimeMillis());
|
||||
|
@ -1498,7 +1503,8 @@ public class MmsDatabase extends MessageDatabase {
|
|||
contentValues.put(EXPIRES_IN, message.getExpiresIn());
|
||||
contentValues.put(VIEW_ONCE, message.isViewOnce());
|
||||
contentValues.put(RECIPIENT_ID, message.getRecipient().getId().serialize());
|
||||
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum());
|
||||
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getCount).sum());
|
||||
contentValues.put(RECEIPT_TIMESTAMP, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getTimestamp).max().orElse(-1));
|
||||
|
||||
if (message.getRecipient().isSelf() && hasAudioAttachment(message.getAttachments())) {
|
||||
contentValues.put(VIEWED_RECEIPT_COUNT, 1L);
|
||||
|
|
|
@ -29,6 +29,7 @@ public interface MmsSmsColumns {
|
|||
public static final String REACTIONS_LAST_SEEN = "reactions_last_seen";
|
||||
public static final String REMOTE_DELETED = "remote_deleted";
|
||||
public static final String SERVER_GUID = "server_guid";
|
||||
public static final String RECEIPT_TIMESTAMP = "receipt_timestamp";
|
||||
|
||||
/**
|
||||
* For storage efficiency, all types are stored within a single 64-bit integer column in the
|
||||
|
|
|
@ -125,8 +125,9 @@ public class SmsDatabase extends MessageDatabase {
|
|||
REACTIONS_UNREAD + " INTEGER DEFAULT 0, " +
|
||||
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
|
||||
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
|
||||
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0," +
|
||||
SERVER_GUID + " TEXT DEFAULT NULL);";
|
||||
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||
SERVER_GUID + " TEXT DEFAULT NULL, " +
|
||||
RECEIPT_TIMESTAMP + " INTEGER DEFAULT -1);";
|
||||
|
||||
public static final String[] CREATE_INDEXS = {
|
||||
"CREATE INDEX IF NOT EXISTS sms_read_and_notified_and_thread_id_index ON " + TABLE_NAME + "(" + READ + "," + NOTIFIED + "," + THREAD_ID + ");",
|
||||
|
@ -488,24 +489,28 @@ public class SmsDatabase extends MessageDatabase {
|
|||
SQLiteDatabase database = databaseHelper.getSignalWritableDatabase();
|
||||
Set<ThreadUpdate> threadUpdates = new HashSet<>();
|
||||
|
||||
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT},
|
||||
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, RECIPIENT_ID, TYPE, DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, RECEIPT_TIMESTAMP},
|
||||
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
|
||||
null, null, null, null)) {
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
|
||||
if (Types.isOutgoingMessageType(CursorUtil.requireLong(cursor, TYPE))) {
|
||||
RecipientId theirRecipientId = messageId.getRecipientId();
|
||||
RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
|
||||
RecipientId outRecipientId = RecipientId.from(CursorUtil.requireLong(cursor, RECIPIENT_ID));
|
||||
|
||||
if (outRecipientId.equals(theirRecipientId)) {
|
||||
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
|
||||
long id = CursorUtil.requireLong(cursor, ID);
|
||||
long threadId = CursorUtil.requireLong(cursor, THREAD_ID);
|
||||
String columnName = receiptType.getColumnName();
|
||||
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0;
|
||||
boolean isFirstIncrement = CursorUtil.requireLong(cursor, columnName) == 0;
|
||||
long savedTimestamp = CursorUtil.requireLong(cursor, RECEIPT_TIMESTAMP);
|
||||
long updatedTimestamp = isFirstIncrement ? Math.max(savedTimestamp, timestamp) : savedTimestamp;
|
||||
|
||||
database.execSQL("UPDATE " + TABLE_NAME +
|
||||
" SET " + columnName + " = " + columnName + " + 1 WHERE " +
|
||||
" SET " + columnName + " = " + columnName + " + 1, " +
|
||||
RECEIPT_TIMESTAMP + " = ? WHERE " +
|
||||
ID + " = ?",
|
||||
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))});
|
||||
SqlUtil.buildArgs(updatedTimestamp, id));
|
||||
|
||||
threadUpdates.add(new ThreadUpdate(threadId, !isFirstIncrement));
|
||||
}
|
||||
|
@ -513,7 +518,7 @@ public class SmsDatabase extends MessageDatabase {
|
|||
}
|
||||
|
||||
if (threadUpdates.size() > 0 && receiptType == ReceiptType.DELIVERY) {
|
||||
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
|
||||
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId(), timestamp);
|
||||
}
|
||||
|
||||
return threadUpdates;
|
||||
|
@ -1201,8 +1206,8 @@ public class SmsDatabase extends MessageDatabase {
|
|||
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
|
||||
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
|
||||
|
||||
RecipientId recipientId = message.getRecipient().getId();
|
||||
Map<RecipientId, Long> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date);
|
||||
RecipientId recipientId = message.getRecipient().getId();
|
||||
Map<RecipientId, EarlyReceiptCache.Receipt> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(date);
|
||||
|
||||
ContentValues contentValues = new ContentValues(6);
|
||||
contentValues.put(RECIPIENT_ID, recipientId.serialize());
|
||||
|
@ -1214,7 +1219,8 @@ public class SmsDatabase extends MessageDatabase {
|
|||
contentValues.put(TYPE, type);
|
||||
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
|
||||
contentValues.put(EXPIRES_IN, message.getExpiresIn());
|
||||
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum());
|
||||
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getCount).sum());
|
||||
contentValues.put(RECEIPT_TIMESTAMP, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getTimestamp).max().orElse(-1));
|
||||
|
||||
long messageId = db.insert(TABLE_NAME, null, contentValues);
|
||||
|
||||
|
|
|
@ -216,8 +216,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
|||
private static final int IDENTITY_MIGRATION = 114;
|
||||
private static final int GROUP_CALL_RING_TABLE = 115;
|
||||
private static final int CLEANUP_SESSION_MIGRATION = 116;
|
||||
private static final int RECEIPT_TIMESTAMP = 117;
|
||||
|
||||
private static final int DATABASE_VERSION = 116;
|
||||
private static final int DATABASE_VERSION = 117;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
|
@ -2040,6 +2041,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
|||
Log.i(TAG, "Cleaned up " + storageCount + " storageIds.");
|
||||
}
|
||||
|
||||
if (oldVersion < RECEIPT_TIMESTAMP) {
|
||||
db.execSQL("ALTER TABLE sms ADD COLUMN receipt_timestamp INTEGER DEFAULT -1");
|
||||
db.execSQL("ALTER TABLE mms ADD COLUMN receipt_timestamp INTEGER DEFAULT -1");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
|
Loading…
Add table
Reference in a new issue