mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-26 00:08:39 +00:00
Add new VIEWED item in RecieptMessage enumeration.
Also includes necessary Database changes for supporting this as well as View-Once receipt support.
This commit is contained in:
@@ -34,6 +34,7 @@ public class GroupReceiptDatabase extends Database {
|
|||||||
public static final int STATUS_UNDELIVERED = 0;
|
public static final int STATUS_UNDELIVERED = 0;
|
||||||
public static final int STATUS_DELIVERED = 1;
|
public static final int STATUS_DELIVERED = 1;
|
||||||
public static final int STATUS_READ = 2;
|
public static final int STATUS_READ = 2;
|
||||||
|
public static final int STATUS_VIEWED = 3;
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
MMS_ID + " INTEGER, " + RECIPIENT_ID + " INTEGER, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
|
MMS_ID + " INTEGER, " + RECIPIENT_ID + " INTEGER, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
|
||||||
|
@@ -117,12 +117,14 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
|||||||
public abstract void markDownloadState(long messageId, long state);
|
public abstract void markDownloadState(long messageId, long state);
|
||||||
public abstract void markIncomingNotificationReceived(long threadId);
|
public abstract void markIncomingNotificationReceived(long threadId);
|
||||||
|
|
||||||
public abstract boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, boolean deliveryReceipt);
|
public abstract boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, @NonNull ReceiptType receiptType);
|
||||||
public abstract List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted);
|
public abstract List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted);
|
||||||
public abstract List<MarkedMessageInfo> setEntireThreadRead(long threadId);
|
public abstract List<MarkedMessageInfo> setEntireThreadRead(long threadId);
|
||||||
public abstract List<MarkedMessageInfo> setMessagesReadSince(long threadId, long timestamp);
|
public abstract List<MarkedMessageInfo> setMessagesReadSince(long threadId, long timestamp);
|
||||||
public abstract List<MarkedMessageInfo> setAllMessagesRead();
|
public abstract List<MarkedMessageInfo> setAllMessagesRead();
|
||||||
public abstract Pair<Long, Long> updateBundleMessageBody(long messageId, String body);
|
public abstract Pair<Long, Long> updateBundleMessageBody(long messageId, String body);
|
||||||
|
public abstract @NonNull List<MarkedMessageInfo> getViewedIncomingMessages(long threadId);
|
||||||
|
public abstract @Nullable MarkedMessageInfo setIncomingMessageViewed(long messageId);
|
||||||
|
|
||||||
public abstract void addFailures(long messageId, List<NetworkFailure> failure);
|
public abstract void addFailures(long messageId, List<NetworkFailure> failure);
|
||||||
public abstract void removeFailure(long messageId, NetworkFailure failure);
|
public abstract void removeFailure(long messageId, NetworkFailure failure);
|
||||||
@@ -555,6 +557,28 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected enum ReceiptType {
|
||||||
|
READ(READ_RECEIPT_COUNT, GroupReceiptDatabase.STATUS_READ),
|
||||||
|
DELIVERY(DELIVERY_RECEIPT_COUNT, GroupReceiptDatabase.STATUS_DELIVERED),
|
||||||
|
VIEWED(VIEWED_RECEIPT_COUNT, GroupReceiptDatabase.STATUS_VIEWED);
|
||||||
|
|
||||||
|
private final String columnName;
|
||||||
|
private final int groupStatus;
|
||||||
|
|
||||||
|
ReceiptType(String columnName, int groupStatus) {
|
||||||
|
this.columnName = columnName;
|
||||||
|
this.groupStatus = groupStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getColumnName() {
|
||||||
|
return columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGroupStatus() {
|
||||||
|
return groupStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class SyncMessageId {
|
public static class SyncMessageId {
|
||||||
|
|
||||||
private final RecipientId recipientId;
|
private final RecipientId recipientId;
|
||||||
|
@@ -185,7 +185,8 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
|
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
|
||||||
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
|
REMOTE_DELETED + " INTEGER DEFAULT 0, " +
|
||||||
MENTIONS_SELF + " INTEGER DEFAULT 0, " +
|
MENTIONS_SELF + " INTEGER DEFAULT 0, " +
|
||||||
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0);";
|
NOTIFIED_TIMESTAMP + " INTEGER DEFAULT 0, " +
|
||||||
|
VIEWED_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
|
||||||
|
|
||||||
public static final String[] CREATE_INDEXS = {
|
public static final String[] CREATE_INDEXS = {
|
||||||
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
||||||
@@ -210,7 +211,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
|
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
|
||||||
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, QUOTE_MENTIONS,
|
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, QUOTE_MENTIONS,
|
||||||
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE, REACTIONS, REACTIONS_UNREAD, REACTIONS_LAST_SEEN,
|
SHARED_CONTACTS, LINK_PREVIEWS, UNIDENTIFIED, VIEW_ONCE, REACTIONS, REACTIONS_UNREAD, REACTIONS_LAST_SEEN,
|
||||||
REMOTE_DELETED, MENTIONS_SELF, NOTIFIED_TIMESTAMP,
|
REMOTE_DELETED, MENTIONS_SELF, NOTIFIED_TIMESTAMP, VIEWED_RECEIPT_COUNT,
|
||||||
"json_group_array(json_object(" +
|
"json_group_array(json_object(" +
|
||||||
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
|
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
|
||||||
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
|
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
|
||||||
@@ -383,6 +384,69 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<MarkedMessageInfo> getViewedIncomingMessages(long threadId) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
String[] columns = new String[]{ID, RECIPIENT_ID, DATE_SENT, MESSAGE_BOX, THREAD_ID};
|
||||||
|
String where = THREAD_ID + " = ? AND " + VIEWED_RECEIPT_COUNT + " > 0 AND " + MESSAGE_BOX + " & " + Types.BASE_INBOX_TYPE + " = " + Types.BASE_INBOX_TYPE;
|
||||||
|
String[] args = SqlUtil.buildArgs(threadId);
|
||||||
|
|
||||||
|
|
||||||
|
try (Cursor cursor = db.query(getTableName(), columns, where, args, null, null, null, null)) {
|
||||||
|
if (cursor == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MarkedMessageInfo> results = new ArrayList<>(cursor.getCount());
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndex(RECIPIENT_ID)));
|
||||||
|
long dateSent = cursor.getLong(cursor.getColumnIndex(DATE_SENT));
|
||||||
|
SyncMessageId syncMessageId = new SyncMessageId(recipientId, dateSent);
|
||||||
|
|
||||||
|
results.add(new MarkedMessageInfo(threadId, syncMessageId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable MarkedMessageInfo setIncomingMessageViewed(long messageId) {
|
||||||
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
|
String[] columns = new String[]{ID, RECIPIENT_ID, DATE_SENT, MESSAGE_BOX, THREAD_ID};
|
||||||
|
String where = ID_WHERE + " AND " + VIEWED_RECEIPT_COUNT + " = 0";
|
||||||
|
String[] args = SqlUtil.buildArgs(messageId);
|
||||||
|
|
||||||
|
database.beginTransaction();
|
||||||
|
try (Cursor cursor = database.query(TABLE_NAME, columns, where, args, null, null, null)) {
|
||||||
|
if (cursor == null || !cursor.moveToFirst()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
long type = CursorUtil.requireLong(cursor, MESSAGE_BOX);
|
||||||
|
if (Types.isSecureType(type) && Types.isInboxType(type)) {
|
||||||
|
long threadId = cursor.getLong(cursor.getColumnIndex(THREAD_ID));
|
||||||
|
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndex(RECIPIENT_ID)));
|
||||||
|
long dateSent = cursor.getLong(cursor.getColumnIndex(DATE_SENT));
|
||||||
|
SyncMessageId syncMessageId = new SyncMessageId(recipientId, dateSent);
|
||||||
|
|
||||||
|
MarkedMessageInfo result = new MarkedMessageInfo(threadId, syncMessageId, null);
|
||||||
|
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(VIEWED_RECEIPT_COUNT, 1);
|
||||||
|
|
||||||
|
database.update(TABLE_NAME, contentValues, where, args);
|
||||||
|
database.setTransactionSuccessful();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
database.endTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull RecipientId address, boolean isVideoOffer) {
|
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull RecipientId address, boolean isVideoOffer) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
@@ -540,23 +604,23 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, boolean deliveryReceipt) {
|
public boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, @NonNull ReceiptType receiptType) {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
||||||
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT},
|
try (Cursor cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, RECIPIENT_ID, receiptType.getColumnName()},
|
||||||
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
|
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
|
||||||
null, null, null, null)) {
|
null, null, null, null)) {
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
|
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
|
||||||
RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
|
RecipientId theirRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
|
||||||
RecipientId ourRecipientId = messageId.getRecipientId();
|
RecipientId ourRecipientId = messageId.getRecipientId();
|
||||||
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
|
String columnName = receiptType.getColumnName();
|
||||||
|
|
||||||
if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) {
|
if (ourRecipientId.equals(theirRecipientId) || Recipient.resolved(theirRecipientId).isGroup()) {
|
||||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
|
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
|
||||||
int status = deliveryReceipt ? GroupReceiptDatabase.STATUS_DELIVERED : GroupReceiptDatabase.STATUS_READ;
|
int status = receiptType.getGroupStatus();
|
||||||
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0;
|
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0;
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
@@ -577,7 +641,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found && deliveryReceipt) {
|
if (!found && receiptType == ReceiptType.DELIVERY) {
|
||||||
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
|
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1808,6 +1872,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
Collections.emptyList(),
|
Collections.emptyList(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
0,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1859,6 +1924,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT));
|
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT));
|
||||||
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT));
|
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT));
|
||||||
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
|
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
|
||||||
|
int viewedReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.VIEWED_RECEIPT_COUNT));
|
||||||
|
|
||||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||||
readReceiptCount = 0;
|
readReceiptCount = 0;
|
||||||
@@ -1880,7 +1946,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, threadId,
|
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, threadId,
|
||||||
contentLocationBytes, messageSize, expiry, status,
|
contentLocationBytes, messageSize, expiry, status,
|
||||||
transactionIdBytes, mailbox, subscriptionId, slideDeck,
|
transactionIdBytes, mailbox, subscriptionId, slideDeck,
|
||||||
readReceiptCount);
|
readReceiptCount, viewedReceiptCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
|
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
|
||||||
@@ -1907,9 +1973,11 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
List<ReactionRecord> reactions = parseReactions(cursor);
|
List<ReactionRecord> reactions = parseReactions(cursor);
|
||||||
boolean mentionsSelf = CursorUtil.requireBoolean(cursor, MENTIONS_SELF);
|
boolean mentionsSelf = CursorUtil.requireBoolean(cursor, MENTIONS_SELF);
|
||||||
long notifiedTimestamp = CursorUtil.requireLong(cursor, NOTIFIED_TIMESTAMP);
|
long notifiedTimestamp = CursorUtil.requireLong(cursor, NOTIFIED_TIMESTAMP);
|
||||||
|
int viewedReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.VIEWED_RECEIPT_COUNT));
|
||||||
|
|
||||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||||
readReceiptCount = 0;
|
readReceiptCount = 0;
|
||||||
|
viewedReceiptCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
|
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
|
||||||
@@ -1928,7 +1996,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||||||
threadId, body, slideDeck, partCount, box, mismatches,
|
threadId, body, slideDeck, partCount, box, mismatches,
|
||||||
networkFailures, subscriptionId, expiresIn, expireStarted,
|
networkFailures, subscriptionId, expiresIn, expireStarted,
|
||||||
isViewOnce, readReceiptCount, quote, contacts, previews, unidentified, reactions,
|
isViewOnce, readReceiptCount, quote, contacts, previews, unidentified, reactions,
|
||||||
remoteDelete, mentionsSelf, notifiedTimestamp);
|
remoteDelete, mentionsSelf, notifiedTimestamp, viewedReceiptCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {
|
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {
|
||||||
|
@@ -14,6 +14,7 @@ public interface MmsSmsColumns {
|
|||||||
public static final String ADDRESS_DEVICE_ID = "address_device_id";
|
public static final String ADDRESS_DEVICE_ID = "address_device_id";
|
||||||
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
|
public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
|
||||||
public static final String READ_RECEIPT_COUNT = "read_receipt_count";
|
public static final String READ_RECEIPT_COUNT = "read_receipt_count";
|
||||||
|
public static final String VIEWED_RECEIPT_COUNT = "viewed_receipt_count";
|
||||||
public static final String MISMATCHED_IDENTITIES = "mismatched_identities";
|
public static final String MISMATCHED_IDENTITIES = "mismatched_identities";
|
||||||
public static final String UNIQUE_ROW_ID = "unique_row_id";
|
public static final String UNIQUE_ROW_ID = "unique_row_id";
|
||||||
public static final String SUBSCRIPTION_ID = "subscription_id";
|
public static final String SUBSCRIPTION_ID = "subscription_id";
|
||||||
|
@@ -101,7 +101,8 @@ public class MmsSmsDatabase extends Database {
|
|||||||
MmsSmsColumns.REACTIONS_LAST_SEEN,
|
MmsSmsColumns.REACTIONS_LAST_SEEN,
|
||||||
MmsSmsColumns.REMOTE_DELETED,
|
MmsSmsColumns.REMOTE_DELETED,
|
||||||
MmsDatabase.MENTIONS_SELF,
|
MmsDatabase.MENTIONS_SELF,
|
||||||
MmsSmsColumns.NOTIFIED_TIMESTAMP};
|
MmsSmsColumns.NOTIFIED_TIMESTAMP,
|
||||||
|
MmsSmsColumns.VIEWED_RECEIPT_COUNT};
|
||||||
|
|
||||||
public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
@@ -339,8 +340,8 @@ public class MmsSmsDatabase extends Database {
|
|||||||
|
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
try {
|
try {
|
||||||
DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, true);
|
DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.DELIVERY);
|
||||||
DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, true);
|
DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.DELIVERY);
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
@@ -379,8 +380,8 @@ public class MmsSmsDatabase extends Database {
|
|||||||
try {
|
try {
|
||||||
boolean handled = false;
|
boolean handled = false;
|
||||||
|
|
||||||
handled |= DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, false);
|
handled |= DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.READ);
|
||||||
handled |= DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, false);
|
handled |= DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.READ);
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
|
||||||
@@ -390,6 +391,35 @@ public class MmsSmsDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A list of ID's that were not updated.
|
||||||
|
*/
|
||||||
|
public @NonNull Collection<SyncMessageId> incrementViewedReceiptCounts(@NonNull List<SyncMessageId> syncMessageIds, long timestamp) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
List<SyncMessageId> unhandled = new LinkedList<>();
|
||||||
|
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
for (SyncMessageId id : syncMessageIds) {
|
||||||
|
boolean handled = incrementViewedReceiptCount(id, timestamp);
|
||||||
|
|
||||||
|
if (!handled) {
|
||||||
|
unhandled.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
return unhandled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean incrementViewedReceiptCount(SyncMessageId syncMessageId, long timestamp) {
|
||||||
|
return DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.VIEWED);
|
||||||
|
}
|
||||||
|
|
||||||
public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) {
|
public int getQuotedMessagePosition(long threadId, long quoteId, @NonNull RecipientId recipientId) {
|
||||||
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
|
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
|
||||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsSmsColumns.REMOTE_DELETED + " = 0";
|
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsSmsColumns.REMOTE_DELETED + " = 0";
|
||||||
@@ -546,7 +576,8 @@ public class MmsSmsDatabase extends Database {
|
|||||||
MmsSmsColumns.DATE_SERVER,
|
MmsSmsColumns.DATE_SERVER,
|
||||||
MmsSmsColumns.REMOTE_DELETED,
|
MmsSmsColumns.REMOTE_DELETED,
|
||||||
MmsDatabase.MENTIONS_SELF,
|
MmsDatabase.MENTIONS_SELF,
|
||||||
MmsSmsColumns.NOTIFIED_TIMESTAMP };
|
MmsSmsColumns.NOTIFIED_TIMESTAMP,
|
||||||
|
MmsSmsColumns.VIEWED_RECEIPT_COUNT};
|
||||||
|
|
||||||
String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
|
String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||||
SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
||||||
@@ -581,7 +612,8 @@ public class MmsSmsDatabase extends Database {
|
|||||||
MmsSmsColumns.DATE_SERVER,
|
MmsSmsColumns.DATE_SERVER,
|
||||||
MmsSmsColumns.REMOTE_DELETED,
|
MmsSmsColumns.REMOTE_DELETED,
|
||||||
MmsDatabase.MENTIONS_SELF,
|
MmsDatabase.MENTIONS_SELF,
|
||||||
MmsSmsColumns.NOTIFIED_TIMESTAMP };
|
MmsSmsColumns.NOTIFIED_TIMESTAMP,
|
||||||
|
MmsSmsColumns.VIEWED_RECEIPT_COUNT};
|
||||||
|
|
||||||
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
||||||
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
||||||
@@ -637,6 +669,7 @@ public class MmsSmsDatabase extends Database {
|
|||||||
mmsColumnsPresent.add(MmsDatabase.REMOTE_DELETED);
|
mmsColumnsPresent.add(MmsDatabase.REMOTE_DELETED);
|
||||||
mmsColumnsPresent.add(MmsDatabase.MENTIONS_SELF);
|
mmsColumnsPresent.add(MmsDatabase.MENTIONS_SELF);
|
||||||
mmsColumnsPresent.add(MmsSmsColumns.NOTIFIED_TIMESTAMP);
|
mmsColumnsPresent.add(MmsSmsColumns.NOTIFIED_TIMESTAMP);
|
||||||
|
mmsColumnsPresent.add(MmsSmsColumns.VIEWED_RECEIPT_COUNT);
|
||||||
|
|
||||||
Set<String> smsColumnsPresent = new HashSet<>();
|
Set<String> smsColumnsPresent = new HashSet<>();
|
||||||
smsColumnsPresent.add(MmsSmsColumns.ID);
|
smsColumnsPresent.add(MmsSmsColumns.ID);
|
||||||
|
@@ -471,7 +471,11 @@ public class SmsDatabase extends MessageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, boolean deliveryReceipt) {
|
public boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, @NonNull ReceiptType receiptType) {
|
||||||
|
if (receiptType == ReceiptType.VIEWED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
boolean foundMessage = false;
|
boolean foundMessage = false;
|
||||||
|
|
||||||
@@ -483,7 +487,7 @@ public class SmsDatabase extends MessageDatabase {
|
|||||||
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
|
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
|
||||||
RecipientId theirRecipientId = messageId.getRecipientId();
|
RecipientId theirRecipientId = messageId.getRecipientId();
|
||||||
RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
|
RecipientId outRecipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)));
|
||||||
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
|
String columnName = receiptType.getColumnName();
|
||||||
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0;
|
boolean isFirstIncrement = cursor.getLong(cursor.getColumnIndexOrThrow(columnName)) == 0;
|
||||||
|
|
||||||
if (outRecipientId.equals(theirRecipientId)) {
|
if (outRecipientId.equals(theirRecipientId)) {
|
||||||
@@ -507,7 +511,7 @@ public class SmsDatabase extends MessageDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundMessage && deliveryReceipt) {
|
if (!foundMessage && receiptType == ReceiptType.DELIVERY) {
|
||||||
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
|
earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getRecipientId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -623,6 +627,16 @@ public class SmsDatabase extends MessageDatabase {
|
|||||||
return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type);
|
return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<MarkedMessageInfo> getViewedIncomingMessages(long threadId) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable MarkedMessageInfo setIncomingMessageViewed(long messageId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Pair<Long, Long> updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) {
|
private Pair<Long, Long> updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) {
|
||||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " +
|
db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " +
|
||||||
|
@@ -160,8 +160,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
private static final int GV1_MIGRATION = 80;
|
private static final int GV1_MIGRATION = 80;
|
||||||
private static final int NOTIFIED_TIMESTAMP = 81;
|
private static final int NOTIFIED_TIMESTAMP = 81;
|
||||||
private static final int GV1_MIGRATION_LAST_SEEN = 82;
|
private static final int GV1_MIGRATION_LAST_SEEN = 82;
|
||||||
|
private static final int VIEWED_RECEIPTS = 83;
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 82;
|
private static final int DATABASE_VERSION = 83;
|
||||||
private static final String DATABASE_NAME = "signal.db";
|
private static final String DATABASE_NAME = "signal.db";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@@ -1170,6 +1171,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL("ALTER TABLE recipient ADD COLUMN last_gv1_migrate_reminder INTEGER DEFAULT 0");
|
db.execSQL("ALTER TABLE recipient ADD COLUMN last_gv1_migrate_reminder INTEGER DEFAULT 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < VIEWED_RECEIPTS) {
|
||||||
|
db.execSQL("ALTER TABLE mms ADD COLUMN viewed_receipt_count INTEGER DEFAULT 0");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
|
@@ -45,10 +45,11 @@ public abstract class DisplayRecord {
|
|||||||
private final int deliveryStatus;
|
private final int deliveryStatus;
|
||||||
private final int deliveryReceiptCount;
|
private final int deliveryReceiptCount;
|
||||||
private final int readReceiptCount;
|
private final int readReceiptCount;
|
||||||
|
private final int viewReceiptCount;
|
||||||
|
|
||||||
DisplayRecord(String body, Recipient recipient, long dateSent,
|
DisplayRecord(String body, Recipient recipient, long dateSent,
|
||||||
long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount,
|
long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount,
|
||||||
long type, int readReceiptCount)
|
long type, int readReceiptCount, int viewReceiptCount)
|
||||||
{
|
{
|
||||||
this.threadId = threadId;
|
this.threadId = threadId;
|
||||||
this.recipient = recipient;
|
this.recipient = recipient;
|
||||||
@@ -59,6 +60,7 @@ public abstract class DisplayRecord {
|
|||||||
this.deliveryReceiptCount = deliveryReceiptCount;
|
this.deliveryReceiptCount = deliveryReceiptCount;
|
||||||
this.readReceiptCount = readReceiptCount;
|
this.readReceiptCount = readReceiptCount;
|
||||||
this.deliveryStatus = deliveryStatus;
|
this.deliveryStatus = deliveryStatus;
|
||||||
|
this.viewReceiptCount = viewReceiptCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull String getBody() {
|
public @NonNull String getBody() {
|
||||||
@@ -188,6 +190,17 @@ public abstract class DisplayRecord {
|
|||||||
return readReceiptCount;
|
return readReceiptCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For outgoing messages, this is incremented whenever a remote recipient has viewed our message
|
||||||
|
* and sends us a VIEWED receipt. For incoming messages, this is an indication of whether local
|
||||||
|
* user has viewed a piece of content.
|
||||||
|
*
|
||||||
|
* @return the number of times this has been viewed.
|
||||||
|
*/
|
||||||
|
public int getViewedReceiptCount() {
|
||||||
|
return viewReceiptCount;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDelivered() {
|
public boolean isDelivered() {
|
||||||
return (deliveryStatus >= SmsDatabase.Status.STATUS_COMPLETE &&
|
return (deliveryStatus >= SmsDatabase.Status.STATUS_COMPLETE &&
|
||||||
deliveryStatus < SmsDatabase.Status.STATUS_PENDING) || deliveryReceiptCount > 0;
|
deliveryStatus < SmsDatabase.Status.STATUS_PENDING) || deliveryReceiptCount > 0;
|
||||||
|
@@ -74,12 +74,13 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
|
|||||||
@NonNull List<ReactionRecord> reactions,
|
@NonNull List<ReactionRecord> reactions,
|
||||||
boolean remoteDelete,
|
boolean remoteDelete,
|
||||||
boolean mentionsSelf,
|
boolean mentionsSelf,
|
||||||
long notifiedTimestamp)
|
long notifiedTimestamp,
|
||||||
|
int viewedReceiptCount)
|
||||||
{
|
{
|
||||||
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
|
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
|
||||||
dateReceived, dateServer, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
|
dateReceived, dateServer, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
|
||||||
subscriptionId, expiresIn, expireStarted, viewOnce, slideDeck,
|
subscriptionId, expiresIn, expireStarted, viewOnce, slideDeck,
|
||||||
readReceiptCount, quote, contacts, linkPreviews, unidentified, reactions, remoteDelete, notifiedTimestamp);
|
readReceiptCount, quote, contacts, linkPreviews, unidentified, reactions, remoteDelete, notifiedTimestamp, viewedReceiptCount);
|
||||||
this.partCount = partCount;
|
this.partCount = partCount;
|
||||||
this.mentionsSelf = mentionsSelf;
|
this.mentionsSelf = mentionsSelf;
|
||||||
}
|
}
|
||||||
|
@@ -93,10 +93,12 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||||||
List<NetworkFailure> networkFailures,
|
List<NetworkFailure> networkFailures,
|
||||||
int subscriptionId, long expiresIn, long expireStarted,
|
int subscriptionId, long expiresIn, long expireStarted,
|
||||||
int readReceiptCount, boolean unidentified,
|
int readReceiptCount, boolean unidentified,
|
||||||
@NonNull List<ReactionRecord> reactions, boolean remoteDelete, long notifiedTimestamp)
|
@NonNull List<ReactionRecord> reactions, boolean remoteDelete, long notifiedTimestamp,
|
||||||
|
int viewedReceiptCount)
|
||||||
{
|
{
|
||||||
super(body, conversationRecipient, dateSent, dateReceived,
|
super(body, conversationRecipient, dateSent, dateReceived,
|
||||||
threadId, deliveryStatus, deliveryReceiptCount, type, readReceiptCount);
|
threadId, deliveryStatus, deliveryReceiptCount, type,
|
||||||
|
readReceiptCount, viewedReceiptCount);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.individualRecipient = individualRecipient;
|
this.individualRecipient = individualRecipient;
|
||||||
this.recipientDeviceId = recipientDeviceId;
|
this.recipientDeviceId = recipientDeviceId;
|
||||||
|
@@ -33,9 +33,10 @@ public abstract class MmsMessageRecord extends MessageRecord {
|
|||||||
@NonNull SlideDeck slideDeck, int readReceiptCount,
|
@NonNull SlideDeck slideDeck, int readReceiptCount,
|
||||||
@Nullable Quote quote, @NonNull List<Contact> contacts,
|
@Nullable Quote quote, @NonNull List<Contact> contacts,
|
||||||
@NonNull List<LinkPreview> linkPreviews, boolean unidentified,
|
@NonNull List<LinkPreview> linkPreviews, boolean unidentified,
|
||||||
@NonNull List<ReactionRecord> reactions, boolean remoteDelete, long notifiedTimestamp)
|
@NonNull List<ReactionRecord> reactions, boolean remoteDelete, long notifiedTimestamp,
|
||||||
|
int viewedReceiptCount)
|
||||||
{
|
{
|
||||||
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp);
|
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, dateServer, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp, viewedReceiptCount);
|
||||||
|
|
||||||
this.slideDeck = slideDeck;
|
this.slideDeck = slideDeck;
|
||||||
this.quote = quote;
|
this.quote = quote;
|
||||||
|
@@ -50,13 +50,14 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
|
|||||||
long dateSent, long dateReceived, int deliveryReceiptCount,
|
long dateSent, long dateReceived, int deliveryReceiptCount,
|
||||||
long threadId, byte[] contentLocation, long messageSize,
|
long threadId, byte[] contentLocation, long messageSize,
|
||||||
long expiry, int status, byte[] transactionId, long mailbox,
|
long expiry, int status, byte[] transactionId, long mailbox,
|
||||||
int subscriptionId, SlideDeck slideDeck, int readReceiptCount)
|
int subscriptionId, SlideDeck slideDeck, int readReceiptCount,
|
||||||
|
int viewedReceiptCount)
|
||||||
{
|
{
|
||||||
super(id, "", conversationRecipient, individualRecipient, recipientDeviceId,
|
super(id, "", conversationRecipient, individualRecipient, recipientDeviceId,
|
||||||
dateSent, dateReceived, -1, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
|
dateSent, dateReceived, -1, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
|
||||||
new LinkedList<>(), new LinkedList<>(), subscriptionId,
|
new LinkedList<>(), new LinkedList<>(), subscriptionId,
|
||||||
0, 0, false, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false,
|
0, 0, false, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false,
|
||||||
Collections.emptyList(), false, 0);
|
Collections.emptyList(), false, 0, viewedReceiptCount);
|
||||||
|
|
||||||
this.contentLocation = contentLocation;
|
this.contentLocation = contentLocation;
|
||||||
this.messageSize = messageSize;
|
this.messageSize = messageSize;
|
||||||
|
@@ -55,7 +55,7 @@ public class SmsMessageRecord extends MessageRecord {
|
|||||||
super(id, body, recipient, individualRecipient, recipientDeviceId,
|
super(id, body, recipient, individualRecipient, recipientDeviceId,
|
||||||
dateSent, dateReceived, dateServer, threadId, status, deliveryReceiptCount, type,
|
dateSent, dateReceived, dateServer, threadId, status, deliveryReceiptCount, type,
|
||||||
mismatches, new LinkedList<>(), subscriptionId,
|
mismatches, new LinkedList<>(), subscriptionId,
|
||||||
expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp);
|
expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getType() {
|
public long getType() {
|
||||||
|
@@ -8,9 +8,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Data {
|
public class Data {
|
||||||
|
|
||||||
@@ -138,6 +140,19 @@ public class Data {
|
|||||||
return longArrays.get(key);
|
return longArrays.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Long> getLongArrayAsList(@NonNull String key) {
|
||||||
|
throwIfAbsent(longArrays, key);
|
||||||
|
|
||||||
|
long[] array = Objects.requireNonNull(longArrays.get(key));
|
||||||
|
List<Long> longs = new ArrayList<>(array.length);
|
||||||
|
|
||||||
|
for (long l : array) {
|
||||||
|
longs.add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
return longs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean hasFloat(@NonNull String key) {
|
public boolean hasFloat(@NonNull String key) {
|
||||||
return floats.containsKey(key);
|
return floats.containsKey(key);
|
||||||
@@ -295,6 +310,17 @@ public class Data {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder putLongListAsArray(@NonNull String key, @NonNull List<Long> value) {
|
||||||
|
long[] longs = new long[value.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < value.size(); i++) {
|
||||||
|
longs[i] = value.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
longArrays.put(key, longs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder putFloat(@NonNull String key, float value) {
|
public Builder putFloat(@NonNull String key, float value) {
|
||||||
floats.put(key, value);
|
floats.put(key, value);
|
||||||
return this;
|
return this;
|
||||||
|
@@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
|
import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||||
@@ -90,6 +91,7 @@ public class WorkManagerFactoryMappings {
|
|||||||
put("RotateSignedPreKeyJob", RotateSignedPreKeyJob.KEY);
|
put("RotateSignedPreKeyJob", RotateSignedPreKeyJob.KEY);
|
||||||
put("SendDeliveryReceiptJob", SendDeliveryReceiptJob.KEY);
|
put("SendDeliveryReceiptJob", SendDeliveryReceiptJob.KEY);
|
||||||
put("SendReadReceiptJob", SendReadReceiptJob.KEY);
|
put("SendReadReceiptJob", SendReadReceiptJob.KEY);
|
||||||
|
put("SendViewedReceiptJob", SendViewedReceiptJob.KEY);
|
||||||
put("ServiceOutageDetectionJob", ServiceOutageDetectionJob.KEY);
|
put("ServiceOutageDetectionJob", ServiceOutageDetectionJob.KEY);
|
||||||
put("SmsReceiveJob", SmsReceiveJob.KEY);
|
put("SmsReceiveJob", SmsReceiveJob.KEY);
|
||||||
put("SmsSendJob", SmsSendJob.KEY);
|
put("SmsSendJob", SmsSendJob.KEY);
|
||||||
|
@@ -120,6 +120,7 @@ public final class JobManagerFactories {
|
|||||||
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
|
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
|
||||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||||
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
|
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
|
||||||
|
put(SendViewedReceiptJob.KEY, new SendViewedReceiptJob.Factory(application));
|
||||||
put(ServiceOutageDetectionJob.KEY, new ServiceOutageDetectionJob.Factory());
|
put(ServiceOutageDetectionJob.KEY, new ServiceOutageDetectionJob.Factory());
|
||||||
put(SmsReceiveJob.KEY, new SmsReceiveJob.Factory());
|
put(SmsReceiveJob.KEY, new SmsReceiveJob.Factory());
|
||||||
put(SmsSendJob.KEY, new SmsSendJob.Factory());
|
put(SmsSendJob.KEY, new SmsSendJob.Factory());
|
||||||
|
@@ -137,6 +137,7 @@ public class PushMediaSendJob extends PushSendJob {
|
|||||||
SyncMessageId id = new SyncMessageId(recipient.getId(), message.getSentTimeMillis());
|
SyncMessageId id = new SyncMessageId(recipient.getId(), message.getSentTimeMillis());
|
||||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
|
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
|
||||||
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
|
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
|
||||||
|
DatabaseFactory.getMmsSmsDatabase(context).incrementViewedReceiptCount(id, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
|
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
|
||||||
|
@@ -433,6 +433,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
|
|
||||||
if (message.isReadReceipt()) handleReadReceipt(content, message);
|
if (message.isReadReceipt()) handleReadReceipt(content, message);
|
||||||
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message);
|
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message);
|
||||||
|
else if (message.isViewedReceipt()) handleViewedReceipt(content, message);
|
||||||
} else if (content.getTypingMessage().isPresent()) {
|
} else if (content.getTypingMessage().isPresent()) {
|
||||||
handleTypingMessage(content, content.getTypingMessage().get());
|
handleTypingMessage(content, content.getTypingMessage().get());
|
||||||
} else {
|
} else {
|
||||||
@@ -1572,6 +1573,29 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||||||
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(RecipientId.fromHighTrust(content.getSender()), message.getTimestamp()));
|
ApplicationDependencies.getJobManager().add(new SendDeliveryReceiptJob(RecipientId.fromHighTrust(content.getSender()), message.getTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleViewedReceipt(@NonNull SignalServiceContent content,
|
||||||
|
@NonNull SignalServiceReceiptMessage message)
|
||||||
|
{
|
||||||
|
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||||
|
log(TAG, "Ignoring viewed receipts for IDs: " + Util.join(message.getTimestamps(), ", "));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(TAG, "Processing viewed reciepts for IDs: " + Util.join(message.getTimestamps(), ","));
|
||||||
|
|
||||||
|
Recipient sender = Recipient.externalHighTrustPush(context, content.getSender());
|
||||||
|
List<SyncMessageId> ids = Stream.of(message.getTimestamps())
|
||||||
|
.map(t -> new SyncMessageId(sender.getId(), t))
|
||||||
|
.toList();
|
||||||
|
Collection<SyncMessageId> unhandled = DatabaseFactory.getMmsSmsDatabase(context)
|
||||||
|
.incrementViewedReceiptCounts(ids, content.getTimestamp());
|
||||||
|
|
||||||
|
for (SyncMessageId id : unhandled) {
|
||||||
|
warn(TAG, String.valueOf(content.getTimestamp()), "[handleViewedReceipt] Could not find matching message! timestamp: " + id.getTimetamp() + " author: " + sender.getId());
|
||||||
|
ApplicationDependencies.getEarlyMessageCache().store(sender.getId(), id.getTimetamp(), content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
|
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceReceiptMessage message)
|
@NonNull SignalServiceReceiptMessage message)
|
||||||
|
@@ -0,0 +1,152 @@
|
|||||||
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
|
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class SendViewedReceiptJob extends BaseJob {
|
||||||
|
|
||||||
|
public static final String KEY = "SendViewedReceiptJob";
|
||||||
|
|
||||||
|
private static final String TAG = SendViewedReceiptJob.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String KEY_THREAD = "thread";
|
||||||
|
private static final String KEY_ADDRESS = "address";
|
||||||
|
private static final String KEY_RECIPIENT = "recipient";
|
||||||
|
private static final String KEY_SYNC_TIMESTAMPS = "message_ids";
|
||||||
|
private static final String KEY_TIMESTAMP = "timestamp";
|
||||||
|
|
||||||
|
private long threadId;
|
||||||
|
private RecipientId recipientId;
|
||||||
|
private List<Long> syncTimestamps;
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public SendViewedReceiptJob(long threadId, @NonNull RecipientId recipientId, long syncTimestamp) {
|
||||||
|
this(threadId, recipientId, Collections.singletonList(syncTimestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SendViewedReceiptJob(long threadId, @NonNull RecipientId recipientId, @NonNull List<Long> syncTimestamps) {
|
||||||
|
this(new Parameters.Builder()
|
||||||
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||||
|
.setMaxAttempts(Parameters.UNLIMITED)
|
||||||
|
.build(),
|
||||||
|
threadId,
|
||||||
|
recipientId,
|
||||||
|
syncTimestamps,
|
||||||
|
System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendViewedReceiptJob(@NonNull Parameters parameters,
|
||||||
|
long threadId,
|
||||||
|
@NonNull RecipientId recipientId,
|
||||||
|
@NonNull List<Long> syncTimestamps,
|
||||||
|
long timestamp)
|
||||||
|
{
|
||||||
|
super(parameters);
|
||||||
|
|
||||||
|
this.threadId = threadId;
|
||||||
|
this.recipientId = recipientId;
|
||||||
|
this.syncTimestamps = syncTimestamps;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Data serialize() {
|
||||||
|
return new Data.Builder().putString(KEY_RECIPIENT, recipientId.serialize())
|
||||||
|
.putLongListAsArray(KEY_SYNC_TIMESTAMPS, syncTimestamps)
|
||||||
|
.putLong(KEY_TIMESTAMP, timestamp)
|
||||||
|
.putLong(KEY_THREAD, threadId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String getFactoryKey() {
|
||||||
|
return KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRun() throws IOException, UntrustedIdentityException {
|
||||||
|
if (!TextSecurePreferences.isReadReceiptsEnabled(context) || syncTimestamps.isEmpty()) return;
|
||||||
|
|
||||||
|
if (!RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||||
|
Log.w(TAG, "Refusing to send receipts to untrusted recipient");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
|
if (recipient.isBlocked()) {
|
||||||
|
Log.w(TAG, "Refusing to send receipts to blocked recipient");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipient.isGroup()) {
|
||||||
|
Log.w(TAG, "Refusing to send receipts to group");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
|
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
|
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED,
|
||||||
|
syncTimestamps,
|
||||||
|
timestamp);
|
||||||
|
|
||||||
|
messageSender.sendReceipt(remoteAddress,
|
||||||
|
UnidentifiedAccessUtil.getAccessFor(context, Recipient.resolved(recipientId)),
|
||||||
|
receiptMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onShouldRetry(@NonNull Exception e) {
|
||||||
|
if (e instanceof PushNetworkException) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure() {
|
||||||
|
Log.w(TAG, "Failed to send read receipts to: " + recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Factory implements Job.Factory<SendViewedReceiptJob> {
|
||||||
|
|
||||||
|
private final Application application;
|
||||||
|
|
||||||
|
public Factory(@NonNull Application application) {
|
||||||
|
this.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull
|
||||||
|
SendViewedReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||||
|
long timestamp = data.getLong(KEY_TIMESTAMP);
|
||||||
|
List<Long> syncTimestamps = data.getLongArrayAsList(KEY_SYNC_TIMESTAMPS);
|
||||||
|
RecipientId recipientId = data.hasString(KEY_RECIPIENT) ? RecipientId.from(data.getString(KEY_RECIPIENT))
|
||||||
|
: Recipient.external(application, data.getString(KEY_ADDRESS)).getId();
|
||||||
|
long threadId = data.getLong(KEY_THREAD);
|
||||||
|
|
||||||
|
return new SendViewedReceiptJob(parameters, threadId, recipientId, syncTimestamps, timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,6 +6,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
|
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
@@ -19,6 +21,7 @@ import org.thoughtcrime.securesms.groups.GroupManager;
|
|||||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
|
||||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||||
@@ -140,6 +143,16 @@ final class MessageRequestRepository {
|
|||||||
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
||||||
MarkReadReceiver.process(context, messageIds);
|
MarkReadReceiver.process(context, messageIds);
|
||||||
|
|
||||||
|
List<MessageDatabase.MarkedMessageInfo> viewedInfos = DatabaseFactory.getMmsDatabase(context)
|
||||||
|
.getViewedIncomingMessages(threadId);
|
||||||
|
|
||||||
|
ApplicationDependencies.getJobManager()
|
||||||
|
.add(new SendViewedReceiptJob(threadId,
|
||||||
|
liveRecipient.getId(),
|
||||||
|
Stream.of(viewedInfos)
|
||||||
|
.map(info -> info.getSyncMessageId().getTimetamp())
|
||||||
|
.toList()));
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
@@ -26,6 +28,12 @@ class ViewOnceMessageRepository {
|
|||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
try (MmsDatabase.Reader reader = MmsDatabase.readerFor(mmsDatabase.getMessageCursor(messageId))) {
|
try (MmsDatabase.Reader reader = MmsDatabase.readerFor(mmsDatabase.getMessageCursor(messageId))) {
|
||||||
MmsMessageRecord record = (MmsMessageRecord) reader.getNext();
|
MmsMessageRecord record = (MmsMessageRecord) reader.getNext();
|
||||||
|
MessageDatabase.MarkedMessageInfo info = mmsDatabase.setIncomingMessageViewed(record.getId());
|
||||||
|
if (info != null) {
|
||||||
|
ApplicationDependencies.getJobManager().add(new SendViewedReceiptJob(record.getThreadId(),
|
||||||
|
info.getSyncMessageId().getRecipientId(),
|
||||||
|
info.getSyncMessageId().getTimetamp()));
|
||||||
|
}
|
||||||
callback.onComplete(Optional.fromNullable(record));
|
callback.onComplete(Optional.fromNullable(record));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -505,6 +505,7 @@ public class MessageSender {
|
|||||||
|
|
||||||
mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis());
|
mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis());
|
||||||
mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis());
|
mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis());
|
||||||
|
mmsSmsDatabase.incrementViewedReceiptCount(syncId, System.currentTimeMillis());
|
||||||
|
|
||||||
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
||||||
mmsDatabase.markExpireStarted(messageId);
|
mmsDatabase.markExpireStarted(messageId);
|
||||||
|
@@ -534,6 +534,7 @@ public class SignalServiceMessageSender {
|
|||||||
|
|
||||||
if (message.isDeliveryReceipt()) builder.setType(ReceiptMessage.Type.DELIVERY);
|
if (message.isDeliveryReceipt()) builder.setType(ReceiptMessage.Type.DELIVERY);
|
||||||
else if (message.isReadReceipt()) builder.setType(ReceiptMessage.Type.READ);
|
else if (message.isReadReceipt()) builder.setType(ReceiptMessage.Type.READ);
|
||||||
|
else if (message.isViewedReceipt()) builder.setType(ReceiptMessage.Type.VIEWED);
|
||||||
|
|
||||||
return container.setReceiptMessage(builder).build().toByteArray();
|
return container.setReceiptMessage(builder).build().toByteArray();
|
||||||
}
|
}
|
||||||
|
@@ -632,6 +632,7 @@ public final class SignalServiceContent {
|
|||||||
|
|
||||||
if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.DELIVERY) type = SignalServiceReceiptMessage.Type.DELIVERY;
|
if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.DELIVERY) type = SignalServiceReceiptMessage.Type.DELIVERY;
|
||||||
else if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.READ) type = SignalServiceReceiptMessage.Type.READ;
|
else if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.READ) type = SignalServiceReceiptMessage.Type.READ;
|
||||||
|
else if (content.getType() == SignalServiceProtos.ReceiptMessage.Type.VIEWED) type = SignalServiceReceiptMessage.Type.VIEWED;
|
||||||
else type = SignalServiceReceiptMessage.Type.UNKNOWN;
|
else type = SignalServiceReceiptMessage.Type.UNKNOWN;
|
||||||
|
|
||||||
return new SignalServiceReceiptMessage(type, content.getTimestampList(), metadata.getTimestamp());
|
return new SignalServiceReceiptMessage(type, content.getTimestampList(), metadata.getTimestamp());
|
||||||
|
@@ -6,7 +6,7 @@ import java.util.List;
|
|||||||
public class SignalServiceReceiptMessage {
|
public class SignalServiceReceiptMessage {
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
UNKNOWN, DELIVERY, READ
|
UNKNOWN, DELIVERY, READ, VIEWED
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
@@ -38,4 +38,8 @@ public class SignalServiceReceiptMessage {
|
|||||||
public boolean isReadReceipt() {
|
public boolean isReadReceipt() {
|
||||||
return type == Type.READ;
|
return type == Type.READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isViewedReceipt() {
|
||||||
|
return type == Type.VIEWED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -279,6 +279,7 @@ message ReceiptMessage {
|
|||||||
enum Type {
|
enum Type {
|
||||||
DELIVERY = 0;
|
DELIVERY = 0;
|
||||||
READ = 1;
|
READ = 1;
|
||||||
|
VIEWED = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional Type type = 1;
|
optional Type type = 1;
|
||||||
|
Reference in New Issue
Block a user