Mark previous messages in thread as read when receiving a read sync message.

This commit is contained in:
Cody Henthorne 2021-04-14 15:42:34 -04:00 committed by Greyson Parrelli
parent d46a9f6d1d
commit ec63dd704a
5 changed files with 40 additions and 16 deletions

View file

@ -49,6 +49,7 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -120,7 +121,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
public abstract void markIncomingNotificationReceived(long threadId); public abstract void markIncomingNotificationReceived(long threadId);
public abstract Set<ThreadUpdate> incrementReceiptCount(SyncMessageId messageId, long timestamp, @NonNull ReceiptType receiptType); public abstract Set<ThreadUpdate> 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, @NonNull Map<Long, Long> threadToLatestRead);
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();

View file

@ -66,6 +66,7 @@ import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteModel; import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo; import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
@ -951,7 +952,7 @@ public class MmsDatabase extends MessageDatabase {
} }
@Override @Override
public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted) { public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map<Long, Long> threadToLatestRead) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
List<Pair<Long, Long>> expiring = new LinkedList<>(); List<Pair<Long, Long>> expiring = new LinkedList<>();
Cursor cursor = null; Cursor cursor = null;
@ -986,6 +987,9 @@ public class MmsDatabase extends MessageDatabase {
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
Long latest = threadToLatestRead.get(threadId);
threadToLatestRead.put(threadId, (latest != null) ? Math.max(latest, messageId.getTimetamp()) : messageId.getTimetamp());
} }
} }
} finally { } finally {

View file

@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo; import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo;
@ -510,7 +511,7 @@ public class SmsDatabase extends MessageDatabase {
} }
@Override @Override
public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted) { public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long proposedExpireStarted, @NonNull Map<Long, Long> threadToLatestRead) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
List<Pair<Long, Long>> expiring = new LinkedList<>(); List<Pair<Long, Long>> expiring = new LinkedList<>();
Cursor cursor = null; Cursor cursor = null;
@ -547,6 +548,9 @@ public class SmsDatabase extends MessageDatabase {
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId); DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId); DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
Long latest = threadToLatestRead.get(threadId);
threadToLatestRead.put(threadId, (latest != null) ? Math.max(latest, messageId.getTimetamp()) : messageId.getTimetamp());
} }
} }
} finally { } finally {

View file

@ -26,6 +26,7 @@ import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
@ -368,18 +369,18 @@ public class ThreadDatabase extends Database {
} }
public List<MarkedMessageInfo> setRead(long threadId, boolean lastSeen) { public List<MarkedMessageInfo> setRead(long threadId, boolean lastSeen) {
return setReadInternal(Collections.singletonList(threadId), lastSeen, -1); return setReadSince(Collections.singletonMap(threadId, -1L), lastSeen);
} }
public List<MarkedMessageInfo> setReadSince(long threadId, boolean lastSeen, long sinceTimestamp) { public List<MarkedMessageInfo> setReadSince(long threadId, boolean lastSeen, long sinceTimestamp) {
return setReadInternal(Collections.singletonList(threadId), lastSeen, sinceTimestamp); return setReadSince(Collections.singletonMap(threadId, sinceTimestamp), lastSeen);
} }
public List<MarkedMessageInfo> setRead(Collection<Long> threadIds, boolean lastSeen) { public List<MarkedMessageInfo> setRead(Collection<Long> threadIds, boolean lastSeen) {
return setReadInternal(threadIds, lastSeen, -1); return setReadSince(Stream.of(threadIds).collect(Collectors.toMap(t -> t, t -> -1L)), lastSeen);
} }
private List<MarkedMessageInfo> setReadInternal(Collection<Long> threadIds, boolean lastSeen, long sinceTimestamp) { public List<MarkedMessageInfo> setReadSince(Map<Long, Long> threadIdToSinceTimestamp, boolean lastSeen) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
List<MarkedMessageInfo> smsRecords = new LinkedList<>(); List<MarkedMessageInfo> smsRecords = new LinkedList<>();
@ -392,11 +393,14 @@ public class ThreadDatabase extends Database {
ContentValues contentValues = new ContentValues(2); ContentValues contentValues = new ContentValues(2);
contentValues.put(READ, ReadStatus.READ.serialize()); contentValues.put(READ, ReadStatus.READ.serialize());
if (lastSeen) { for (Map.Entry<Long, Long> entry : threadIdToSinceTimestamp.entrySet()) {
contentValues.put(LAST_SEEN, sinceTimestamp == -1 ? System.currentTimeMillis() : sinceTimestamp); long threadId = entry.getKey();
} long sinceTimestamp = entry.getValue();
if (lastSeen) {
contentValues.put(LAST_SEEN, sinceTimestamp == -1 ? System.currentTimeMillis() : sinceTimestamp);
}
for (long threadId : threadIds) {
ThreadRecord previous = getThreadRecord(threadId); ThreadRecord previous = getThreadRecord(threadId);
smsRecords.addAll(DatabaseFactory.getSmsDatabase(context).setMessagesReadSince(threadId, sinceTimestamp)); smsRecords.addAll(DatabaseFactory.getSmsDatabase(context).setMessagesReadSince(threadId, sinceTimestamp));
@ -422,7 +426,7 @@ public class ThreadDatabase extends Database {
db.endTransaction(); db.endTransaction();
} }
notifyConversationListeners(new HashSet<>(threadIds)); notifyConversationListeners(threadIdToSinceTimestamp.keySet());
notifyConversationListListeners(); notifyConversationListListeners();
if (needsSync) { if (needsSync) {

View file

@ -14,13 +14,11 @@ import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.signal.ringrtc.CallId; import org.signal.ringrtc.CallId;
import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment; import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.attachments.TombstoneAttachment; import org.thoughtcrime.securesms.attachments.TombstoneAttachment;
import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.components.emoji.Emoji;
import org.thoughtcrime.securesms.components.emoji.EmojiUtil; import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
import org.thoughtcrime.securesms.contactshare.Contact; import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.contactshare.ContactModelMapper; import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
@ -84,6 +82,7 @@ import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteModel; import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.StickerSlide; import org.thoughtcrime.securesms.mms.StickerSlide;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.payments.MobileCoinPublicAddress; import org.thoughtcrime.securesms.payments.MobileCoinPublicAddress;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -143,6 +142,7 @@ import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -987,9 +987,14 @@ public final class MessageContentProcessor {
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp) private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
{ {
Map<Long, Long> threadToLatestRead = new HashMap<>();
for (ReadMessage readMessage : readMessages) { for (ReadMessage readMessage : readMessages) {
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp); List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()),
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()), envelopeTimestamp); envelopeTimestamp,
threadToLatestRead);
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Recipient.externalPush(context, readMessage.getSender()).getId(), readMessage.getTimestamp()),
envelopeTimestamp,
threadToLatestRead);
for (Pair<Long, Long> expiringMessage : expiringText) { for (Pair<Long, Long> expiringMessage : expiringText) {
ApplicationDependencies.getExpiringMessageManager() ApplicationDependencies.getExpiringMessageManager()
@ -1002,6 +1007,12 @@ public final class MessageContentProcessor {
} }
} }
List<MessageDatabase.MarkedMessageInfo> markedMessages = DatabaseFactory.getThreadDatabase(context).setReadSince(threadToLatestRead, false);
if (Util.hasItems(markedMessages)) {
Log.i(TAG, "Updating past messages: " + markedMessages.size());
MarkReadReceiver.process(context, markedMessages);
}
MessageNotifier messageNotifier = ApplicationDependencies.getMessageNotifier(); MessageNotifier messageNotifier = ApplicationDependencies.getMessageNotifier();
messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
messageNotifier.cancelDelayedNotifications(); messageNotifier.cancelDelayedNotifications();