kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add new VIEWED item in RecieptMessage enumeration.
Also includes necessary Database changes for supporting this as well as View-Once receipt support.fork-5.53.8
rodzic
7bb1262571
commit
ce44e3949c
|
@ -34,6 +34,7 @@ public class GroupReceiptDatabase extends Database {
|
|||
public static final int STATUS_UNDELIVERED = 0;
|
||||
public static final int STATUS_DELIVERED = 1;
|
||||
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, " +
|
||||
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 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<MarkedMessageInfo> setEntireThreadRead(long threadId);
|
||||
public abstract List<MarkedMessageInfo> setMessagesReadSince(long threadId, long timestamp);
|
||||
public abstract List<MarkedMessageInfo> setAllMessagesRead();
|
||||
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 removeFailure(long messageId, NetworkFailure failure);
|
||||
|
@ -555,6 +557,28 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
|||
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 {
|
||||
|
||||
private final RecipientId recipientId;
|
||||
|
|
|
@ -185,7 +185,8 @@ public class MmsDatabase extends MessageDatabase {
|
|||
REACTIONS_LAST_SEEN + " INTEGER DEFAULT -1, " +
|
||||
REMOTE_DELETED + " 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 = {
|
||||
"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,
|
||||
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,
|
||||
REMOTE_DELETED, MENTIONS_SELF, NOTIFIED_TIMESTAMP,
|
||||
REMOTE_DELETED, MENTIONS_SELF, NOTIFIED_TIMESTAMP, VIEWED_RECEIPT_COUNT,
|
||||
"json_group_array(json_object(" +
|
||||
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
|
||||
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
|
||||
|
@ -383,6 +384,69 @@ public class MmsDatabase extends MessageDatabase {
|
|||
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
|
||||
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull RecipientId address, boolean isVideoOffer) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -540,23 +604,23 @@ public class MmsDatabase extends MessageDatabase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, boolean deliveryReceipt) {
|
||||
public boolean incrementReceiptCount(SyncMessageId messageId, long timestamp, @NonNull ReceiptType receiptType) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
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())},
|
||||
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)));
|
||||
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()) {
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(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;
|
||||
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
|
@ -1808,6 +1872,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
Collections.emptyList(),
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
@ -1859,6 +1924,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT));
|
||||
int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT));
|
||||
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
|
||||
int viewedReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.VIEWED_RECEIPT_COUNT));
|
||||
|
||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||
readReceiptCount = 0;
|
||||
|
@ -1880,7 +1946,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, threadId,
|
||||
contentLocationBytes, messageSize, expiry, status,
|
||||
transactionIdBytes, mailbox, subscriptionId, slideDeck,
|
||||
readReceiptCount);
|
||||
readReceiptCount, viewedReceiptCount);
|
||||
}
|
||||
|
||||
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
|
||||
|
@ -1907,9 +1973,11 @@ public class MmsDatabase extends MessageDatabase {
|
|||
List<ReactionRecord> reactions = parseReactions(cursor);
|
||||
boolean mentionsSelf = CursorUtil.requireBoolean(cursor, MENTIONS_SELF);
|
||||
long notifiedTimestamp = CursorUtil.requireLong(cursor, NOTIFIED_TIMESTAMP);
|
||||
int viewedReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.VIEWED_RECEIPT_COUNT));
|
||||
|
||||
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
|
||||
readReceiptCount = 0;
|
||||
readReceiptCount = 0;
|
||||
viewedReceiptCount = 0;
|
||||
}
|
||||
|
||||
Recipient recipient = Recipient.live(RecipientId.from(recipientId)).get();
|
||||
|
@ -1928,7 +1996,7 @@ public class MmsDatabase extends MessageDatabase {
|
|||
threadId, body, slideDeck, partCount, box, mismatches,
|
||||
networkFailures, subscriptionId, expiresIn, expireStarted,
|
||||
isViewOnce, readReceiptCount, quote, contacts, previews, unidentified, reactions,
|
||||
remoteDelete, mentionsSelf, notifiedTimestamp);
|
||||
remoteDelete, mentionsSelf, notifiedTimestamp, viewedReceiptCount);
|
||||
}
|
||||
|
||||
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 DELIVERY_RECEIPT_COUNT = "delivery_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 UNIQUE_ROW_ID = "unique_row_id";
|
||||
public static final String SUBSCRIPTION_ID = "subscription_id";
|
||||
|
|
|
@ -101,7 +101,8 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsSmsColumns.REACTIONS_LAST_SEEN,
|
||||
MmsSmsColumns.REMOTE_DELETED,
|
||||
MmsDatabase.MENTIONS_SELF,
|
||||
MmsSmsColumns.NOTIFIED_TIMESTAMP};
|
||||
MmsSmsColumns.NOTIFIED_TIMESTAMP,
|
||||
MmsSmsColumns.VIEWED_RECEIPT_COUNT};
|
||||
|
||||
public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
|
||||
super(context, databaseHelper);
|
||||
|
@ -339,8 +340,8 @@ public class MmsSmsDatabase extends Database {
|
|||
|
||||
db.beginTransaction();
|
||||
try {
|
||||
DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, true);
|
||||
DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, true);
|
||||
DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.DELIVERY);
|
||||
DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.DELIVERY);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
@ -379,8 +380,8 @@ public class MmsSmsDatabase extends Database {
|
|||
try {
|
||||
boolean handled = false;
|
||||
|
||||
handled |= DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, false);
|
||||
handled |= DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, false);
|
||||
handled |= DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.READ);
|
||||
handled |= DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, timestamp, MessageDatabase.ReceiptType.READ);
|
||||
|
||||
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) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + MmsSmsColumns.REMOTE_DELETED + " = 0";
|
||||
|
@ -546,7 +576,8 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsSmsColumns.DATE_SERVER,
|
||||
MmsSmsColumns.REMOTE_DELETED,
|
||||
MmsDatabase.MENTIONS_SELF,
|
||||
MmsSmsColumns.NOTIFIED_TIMESTAMP };
|
||||
MmsSmsColumns.NOTIFIED_TIMESTAMP,
|
||||
MmsSmsColumns.VIEWED_RECEIPT_COUNT};
|
||||
|
||||
String[] smsProjection = {SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
|
||||
SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
|
||||
|
@ -581,7 +612,8 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsSmsColumns.DATE_SERVER,
|
||||
MmsSmsColumns.REMOTE_DELETED,
|
||||
MmsDatabase.MENTIONS_SELF,
|
||||
MmsSmsColumns.NOTIFIED_TIMESTAMP };
|
||||
MmsSmsColumns.NOTIFIED_TIMESTAMP,
|
||||
MmsSmsColumns.VIEWED_RECEIPT_COUNT};
|
||||
|
||||
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
||||
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
||||
|
@ -637,6 +669,7 @@ public class MmsSmsDatabase extends Database {
|
|||
mmsColumnsPresent.add(MmsDatabase.REMOTE_DELETED);
|
||||
mmsColumnsPresent.add(MmsDatabase.MENTIONS_SELF);
|
||||
mmsColumnsPresent.add(MmsSmsColumns.NOTIFIED_TIMESTAMP);
|
||||
mmsColumnsPresent.add(MmsSmsColumns.VIEWED_RECEIPT_COUNT);
|
||||
|
||||
Set<String> smsColumnsPresent = new HashSet<>();
|
||||
smsColumnsPresent.add(MmsSmsColumns.ID);
|
||||
|
|
|
@ -471,7 +471,11 @@ public class SmsDatabase extends MessageDatabase {
|
|||
}
|
||||
|
||||
@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();
|
||||
boolean foundMessage = false;
|
||||
|
||||
|
@ -483,7 +487,7 @@ public class SmsDatabase extends MessageDatabase {
|
|||
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
|
||||
RecipientId theirRecipientId = messageId.getRecipientId();
|
||||
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;
|
||||
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
|
@ -623,6 +627,16 @@ public class SmsDatabase extends MessageDatabase {
|
|||
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) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
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 NOTIFIED_TIMESTAMP = 81;
|
||||
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 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");
|
||||
}
|
||||
|
||||
if (oldVersion < VIEWED_RECEIPTS) {
|
||||
db.execSQL("ALTER TABLE mms ADD COLUMN viewed_receipt_count INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
|
|
@ -45,10 +45,11 @@ public abstract class DisplayRecord {
|
|||
private final int deliveryStatus;
|
||||
private final int deliveryReceiptCount;
|
||||
private final int readReceiptCount;
|
||||
private final int viewReceiptCount;
|
||||
|
||||
DisplayRecord(String body, Recipient recipient, long dateSent,
|
||||
long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount,
|
||||
long type, int readReceiptCount)
|
||||
long type, int readReceiptCount, int viewReceiptCount)
|
||||
{
|
||||
this.threadId = threadId;
|
||||
this.recipient = recipient;
|
||||
|
@ -59,6 +60,7 @@ public abstract class DisplayRecord {
|
|||
this.deliveryReceiptCount = deliveryReceiptCount;
|
||||
this.readReceiptCount = readReceiptCount;
|
||||
this.deliveryStatus = deliveryStatus;
|
||||
this.viewReceiptCount = viewReceiptCount;
|
||||
}
|
||||
|
||||
public @NonNull String getBody() {
|
||||
|
@ -188,6 +190,17 @@ public abstract class DisplayRecord {
|
|||
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() {
|
||||
return (deliveryStatus >= SmsDatabase.Status.STATUS_COMPLETE &&
|
||||
deliveryStatus < SmsDatabase.Status.STATUS_PENDING) || deliveryReceiptCount > 0;
|
||||
|
|
|
@ -74,12 +74,13 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
|
|||
@NonNull List<ReactionRecord> reactions,
|
||||
boolean remoteDelete,
|
||||
boolean mentionsSelf,
|
||||
long notifiedTimestamp)
|
||||
long notifiedTimestamp,
|
||||
int viewedReceiptCount)
|
||||
{
|
||||
super(id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
|
||||
dateReceived, dateServer, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
|
||||
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.mentionsSelf = mentionsSelf;
|
||||
}
|
||||
|
|
|
@ -93,10 +93,12 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
List<NetworkFailure> networkFailures,
|
||||
int subscriptionId, long expiresIn, long expireStarted,
|
||||
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,
|
||||
threadId, deliveryStatus, deliveryReceiptCount, type, readReceiptCount);
|
||||
threadId, deliveryStatus, deliveryReceiptCount, type,
|
||||
readReceiptCount, viewedReceiptCount);
|
||||
this.id = id;
|
||||
this.individualRecipient = individualRecipient;
|
||||
this.recipientDeviceId = recipientDeviceId;
|
||||
|
|
|
@ -33,9 +33,10 @@ public abstract class MmsMessageRecord extends MessageRecord {
|
|||
@NonNull SlideDeck slideDeck, int readReceiptCount,
|
||||
@Nullable Quote quote, @NonNull List<Contact> contacts,
|
||||
@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.quote = quote;
|
||||
|
|
|
@ -50,13 +50,14 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
|
|||
long dateSent, long dateReceived, int deliveryReceiptCount,
|
||||
long threadId, byte[] contentLocation, long messageSize,
|
||||
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,
|
||||
dateSent, dateReceived, -1, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
|
||||
new LinkedList<>(), new LinkedList<>(), subscriptionId,
|
||||
0, 0, false, slideDeck, readReceiptCount, null, Collections.emptyList(), Collections.emptyList(), false,
|
||||
Collections.emptyList(), false, 0);
|
||||
Collections.emptyList(), false, 0, viewedReceiptCount);
|
||||
|
||||
this.contentLocation = contentLocation;
|
||||
this.messageSize = messageSize;
|
||||
|
|
|
@ -55,7 +55,7 @@ public class SmsMessageRecord extends MessageRecord {
|
|||
super(id, body, recipient, individualRecipient, recipientDeviceId,
|
||||
dateSent, dateReceived, dateServer, threadId, status, deliveryReceiptCount, type,
|
||||
mismatches, new LinkedList<>(), subscriptionId,
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp);
|
||||
expiresIn, expireStarted, readReceiptCount, unidentified, reactions, remoteDelete, notifiedTimestamp, 0);
|
||||
}
|
||||
|
||||
public long getType() {
|
||||
|
|
|
@ -8,9 +8,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Data {
|
||||
|
||||
|
@ -138,6 +140,19 @@ public class Data {
|
|||
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) {
|
||||
return floats.containsKey(key);
|
||||
|
@ -295,6 +310,17 @@ public class Data {
|
|||
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) {
|
||||
floats.put(key, value);
|
||||
return this;
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
|||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
|
||||
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
||||
import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
|
||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||
|
@ -90,6 +91,7 @@ public class WorkManagerFactoryMappings {
|
|||
put("RotateSignedPreKeyJob", RotateSignedPreKeyJob.KEY);
|
||||
put("SendDeliveryReceiptJob", SendDeliveryReceiptJob.KEY);
|
||||
put("SendReadReceiptJob", SendReadReceiptJob.KEY);
|
||||
put("SendViewedReceiptJob", SendViewedReceiptJob.KEY);
|
||||
put("ServiceOutageDetectionJob", ServiceOutageDetectionJob.KEY);
|
||||
put("SmsReceiveJob", SmsReceiveJob.KEY);
|
||||
put("SmsSendJob", SmsSendJob.KEY);
|
||||
|
|
|
@ -120,6 +120,7 @@ public final class JobManagerFactories {
|
|||
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
|
||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
|
||||
put(SendViewedReceiptJob.KEY, new SendViewedReceiptJob.Factory(application));
|
||||
put(ServiceOutageDetectionJob.KEY, new ServiceOutageDetectionJob.Factory());
|
||||
put(SmsReceiveJob.KEY, new SmsReceiveJob.Factory());
|
||||
put(SmsSendJob.KEY, new SmsSendJob.Factory());
|
||||
|
|
|
@ -137,6 +137,7 @@ public class PushMediaSendJob extends PushSendJob {
|
|||
SyncMessageId id = new SyncMessageId(recipient.getId(), message.getSentTimeMillis());
|
||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
|
||||
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
|
||||
DatabaseFactory.getMmsSmsDatabase(context).incrementViewedReceiptCount(id, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) {
|
||||
|
|
|
@ -433,6 +433,7 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
|
||||
if (message.isReadReceipt()) handleReadReceipt(content, message);
|
||||
else if (message.isDeliveryReceipt()) handleDeliveryReceipt(content, message);
|
||||
else if (message.isViewedReceipt()) handleViewedReceipt(content, message);
|
||||
} else if (content.getTypingMessage().isPresent()) {
|
||||
handleTypingMessage(content, content.getTypingMessage().get());
|
||||
} else {
|
||||
|
@ -1572,6 +1573,29 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
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")
|
||||
private void handleDeliveryReceipt(@NonNull SignalServiceContent content,
|
||||
@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.core.util.Consumer;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
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.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
|
@ -140,6 +143,16 @@ final class MessageRequestRepository {
|
|||
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
||||
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)) {
|
||||
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.MmsDatabase;
|
||||
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.util.concurrent.SignalExecutors;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
@ -26,6 +28,12 @@ class ViewOnceMessageRepository {
|
|||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
try (MmsDatabase.Reader reader = MmsDatabase.readerFor(mmsDatabase.getMessageCursor(messageId))) {
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -505,6 +505,7 @@ public class MessageSender {
|
|||
|
||||
mmsSmsDatabase.incrementDeliveryReceiptCount(syncId, System.currentTimeMillis());
|
||||
mmsSmsDatabase.incrementReadReceiptCount(syncId, System.currentTimeMillis());
|
||||
mmsSmsDatabase.incrementViewedReceiptCount(syncId, System.currentTimeMillis());
|
||||
|
||||
if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
|
||||
mmsDatabase.markExpireStarted(messageId);
|
||||
|
|
|
@ -534,6 +534,7 @@ public class SignalServiceMessageSender {
|
|||
|
||||
if (message.isDeliveryReceipt()) builder.setType(ReceiptMessage.Type.DELIVERY);
|
||||
else if (message.isReadReceipt()) builder.setType(ReceiptMessage.Type.READ);
|
||||
else if (message.isViewedReceipt()) builder.setType(ReceiptMessage.Type.VIEWED);
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
return new SignalServiceReceiptMessage(type, content.getTimestampList(), metadata.getTimestamp());
|
||||
|
|
|
@ -6,7 +6,7 @@ import java.util.List;
|
|||
public class SignalServiceReceiptMessage {
|
||||
|
||||
public enum Type {
|
||||
UNKNOWN, DELIVERY, READ
|
||||
UNKNOWN, DELIVERY, READ, VIEWED
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
@ -38,4 +38,8 @@ public class SignalServiceReceiptMessage {
|
|||
public boolean isReadReceipt() {
|
||||
return type == Type.READ;
|
||||
}
|
||||
|
||||
public boolean isViewedReceipt() {
|
||||
return type == Type.VIEWED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -279,6 +279,7 @@ message ReceiptMessage {
|
|||
enum Type {
|
||||
DELIVERY = 0;
|
||||
READ = 1;
|
||||
VIEWED = 2;
|
||||
}
|
||||
|
||||
optional Type type = 1;
|
||||
|
|
Ładowanie…
Reference in New Issue