mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 04:58:45 +00:00
Fix crash from external share.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
package org.thoughtcrime.securesms.conversation
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
|
||||
/**
|
||||
* Represents metadata about a conversation.
|
||||
*/
|
||||
data class ConversationData(
|
||||
val threadRecipient: Recipient,
|
||||
val threadId: Long,
|
||||
val lastSeen: Long,
|
||||
val lastSeenPosition: Int,
|
||||
|
||||
@@ -13,6 +13,12 @@ import org.signal.paging.PagedDataSource;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationData.MessageRequestData;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AttachmentHelper;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.CallHelper;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.MentionHelper;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.PaymentHelper;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.QuotedHelper;
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ReactionHelper;
|
||||
import org.thoughtcrime.securesms.database.CallTable;
|
||||
import org.thoughtcrime.securesms.database.MessageTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
@@ -24,25 +30,15 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||
import org.thoughtcrime.securesms.database.model.UpdateDescription;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.payments.Payment;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Core data source for loading an individual conversation.
|
||||
@@ -59,14 +55,22 @@ public class ConversationDataSource implements PagedDataSource<MessageId, Conver
|
||||
/** Used once for the initial fetch, then cleared. */
|
||||
private int baseSize;
|
||||
|
||||
private Recipient threadRecipient;
|
||||
private final Recipient threadRecipient;
|
||||
|
||||
public ConversationDataSource(@NonNull Context context, long threadId, @NonNull MessageRequestData messageRequestData, boolean showUniversalExpireTimerUpdate, int baseSize) {
|
||||
public ConversationDataSource(
|
||||
@NonNull Context context,
|
||||
long threadId,
|
||||
@NonNull MessageRequestData messageRequestData,
|
||||
boolean showUniversalExpireTimerUpdate,
|
||||
int baseSize,
|
||||
@NonNull Recipient threadRecipient
|
||||
) {
|
||||
this.context = context;
|
||||
this.threadId = threadId;
|
||||
this.messageRequestData = messageRequestData;
|
||||
this.showUniversalExpireTimerUpdate = showUniversalExpireTimerUpdate;
|
||||
this.baseSize = baseSize;
|
||||
this.threadRecipient = threadRecipient;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,8 +172,6 @@ public class ConversationDataSource implements PagedDataSource<MessageId, Conver
|
||||
records = callHelper.buildUpdatedModels(records);
|
||||
stopwatch.split("call-models");
|
||||
|
||||
ensureThreadRecipient();
|
||||
|
||||
for (ServiceId serviceId : referencedIds) {
|
||||
Recipient.resolved(RecipientId.from(serviceId));
|
||||
}
|
||||
@@ -234,7 +236,6 @@ public class ConversationDataSource implements PagedDataSource<MessageId, Conver
|
||||
|
||||
stopwatch.split("calls");
|
||||
|
||||
ensureThreadRecipient();
|
||||
return ConversationMessage.ConversationMessageFactory.createWithUnresolvedData(ApplicationDependencies.getApplication(),
|
||||
record,
|
||||
record.getDisplayBody(ApplicationDependencies.getApplication()),
|
||||
@@ -253,197 +254,4 @@ public class ConversationDataSource implements PagedDataSource<MessageId, Conver
|
||||
public @NonNull MessageId getKey(@NonNull ConversationMessage conversationMessage) {
|
||||
return new MessageId(conversationMessage.getMessageRecord().getId());
|
||||
}
|
||||
|
||||
private static class MentionHelper {
|
||||
|
||||
private Collection<Long> messageIds = new LinkedList<>();
|
||||
private Map<Long, List<Mention>> messageIdToMentions = new HashMap<>();
|
||||
|
||||
void add(MessageRecord record) {
|
||||
if (record.isMms()) {
|
||||
messageIds.add(record.getId());
|
||||
}
|
||||
}
|
||||
|
||||
void fetchMentions(Context context) {
|
||||
messageIdToMentions = SignalDatabase.mentions().getMentionsForMessages(messageIds);
|
||||
}
|
||||
|
||||
@Nullable List<Mention> getMentions(long id) {
|
||||
return messageIdToMentions.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureThreadRecipient() {
|
||||
if (threadRecipient == null) {
|
||||
threadRecipient = Objects.requireNonNull(SignalDatabase.threads().getRecipientForThreadId(threadId));
|
||||
}
|
||||
}
|
||||
|
||||
private static class QuotedHelper {
|
||||
|
||||
private Collection<MessageRecord> records = new LinkedList<>();
|
||||
private Set<Long> hasBeenQuotedIds = new HashSet<>();
|
||||
|
||||
void add(MessageRecord record) {
|
||||
records.add(record);
|
||||
}
|
||||
|
||||
void fetchQuotedState() {
|
||||
hasBeenQuotedIds = SignalDatabase.messages().isQuoted(records);
|
||||
}
|
||||
|
||||
boolean isQuoted(long id) {
|
||||
return hasBeenQuotedIds.contains(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AttachmentHelper {
|
||||
|
||||
private Collection<Long> messageIds = new LinkedList<>();
|
||||
private Map<Long, List<DatabaseAttachment>> messageIdToAttachments = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord record) {
|
||||
if (record.isMms()) {
|
||||
messageIds.add(record.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void addAll(List<MessageRecord> records) {
|
||||
for (MessageRecord record : records) {
|
||||
add(record);
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchAttachments() {
|
||||
messageIdToAttachments = SignalDatabase.attachments().getAttachmentsForMessages(messageIds);
|
||||
}
|
||||
|
||||
public @NonNull List<MessageRecord> buildUpdatedModels(@NonNull Context context, @NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
if (record instanceof MediaMmsMessageRecord) {
|
||||
List<DatabaseAttachment> attachments = messageIdToAttachments.get(record.getId());
|
||||
|
||||
if (Util.hasItems(attachments)) {
|
||||
return ((MediaMmsMessageRecord) record).withAttachments(context, attachments);
|
||||
}
|
||||
}
|
||||
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReactionHelper {
|
||||
|
||||
private Collection<MessageId> messageIds = new LinkedList<>();
|
||||
private Map<MessageId, List<ReactionRecord>> messageIdToReactions = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord record) {
|
||||
messageIds.add(new MessageId(record.getId()));
|
||||
}
|
||||
|
||||
public void addAll(List<MessageRecord> records) {
|
||||
for (MessageRecord record : records) {
|
||||
add(record);
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchReactions() {
|
||||
messageIdToReactions = SignalDatabase.reactions().getReactionsForMessages(messageIds);
|
||||
}
|
||||
|
||||
public @NonNull List<MessageRecord> buildUpdatedModels(@NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
MessageId messageId = new MessageId(record.getId());
|
||||
List<ReactionRecord> reactions = messageIdToReactions.get(messageId);
|
||||
|
||||
return recordWithReactions(record, reactions);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static MessageRecord recordWithReactions(@NonNull MessageRecord record, List<ReactionRecord> reactions) {
|
||||
if (Util.hasItems(reactions)) {
|
||||
if (record instanceof MediaMmsMessageRecord) {
|
||||
return ((MediaMmsMessageRecord) record).withReactions(reactions);
|
||||
} else {
|
||||
throw new IllegalStateException("We have reactions for an unsupported record type: " + record.getClass().getName());
|
||||
}
|
||||
} else {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class PaymentHelper {
|
||||
private final Map<UUID, Long> paymentMessages = new HashMap<>();
|
||||
private final Map<Long, Payment> messageIdToPayment = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord messageRecord) {
|
||||
if (messageRecord.isMms() && messageRecord.isPaymentNotification()) {
|
||||
UUID paymentUuid = UuidUtil.parseOrNull(messageRecord.getBody());
|
||||
if (paymentUuid != null) {
|
||||
paymentMessages.put(paymentUuid, messageRecord.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchPayments() {
|
||||
List<Payment> payments = SignalDatabase.payments().getPayments(paymentMessages.keySet());
|
||||
for (Payment payment : payments) {
|
||||
if (payment != null) {
|
||||
messageIdToPayment.put(paymentMessages.get(payment.getUuid()), payment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull List<MessageRecord> buildUpdatedModels(@NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
if (record instanceof MediaMmsMessageRecord) {
|
||||
Payment payment = messageIdToPayment.get(record.getId());
|
||||
if (payment != null) {
|
||||
return ((MediaMmsMessageRecord) record).withPayment(payment);
|
||||
}
|
||||
}
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
private static class CallHelper {
|
||||
private final Collection<Long> messageIds = new LinkedList<>();
|
||||
private Map<Long, CallTable.Call> messageIdToCall = Collections.emptyMap();
|
||||
|
||||
public void add(MessageRecord messageRecord) {
|
||||
if (messageRecord.isCallLog() && !messageRecord.isGroupCall()) {
|
||||
messageIds.add(messageRecord.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchCalls() {
|
||||
if (!messageIds.isEmpty()) {
|
||||
messageIdToCall = SignalDatabase.calls().getCalls(messageIds);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull List<MessageRecord> buildUpdatedModels(@NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
if (record.isCallLog() && record instanceof MediaMmsMessageRecord) {
|
||||
CallTable.Call call = messageIdToCall.get(record.getId());
|
||||
if (call != null) {
|
||||
return ((MediaMmsMessageRecord) record).withCall(call);
|
||||
}
|
||||
}
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,8 @@ public class ConversationRepository {
|
||||
|
||||
@WorkerThread
|
||||
public @NonNull ConversationData getConversationData(long threadId, @NonNull Recipient conversationRecipient, int jumpToPosition) {
|
||||
ThreadTable.ConversationMetadata metadata = SignalDatabase.threads().getConversationMetadata(threadId);
|
||||
int threadSize = SignalDatabase.messages().getMessageCountForThread(threadId);
|
||||
ThreadTable.ConversationMetadata metadata = SignalDatabase.threads().getConversationMetadata(threadId);
|
||||
int threadSize = SignalDatabase.messages().getMessageCountForThread(threadId);
|
||||
long lastSeen = metadata.getLastSeen();
|
||||
int lastSeenPosition = 0;
|
||||
long lastScrolled = metadata.getLastScrolled();
|
||||
@@ -118,7 +118,7 @@ public class ConversationRepository {
|
||||
showUniversalExpireTimerUpdate = true;
|
||||
}
|
||||
|
||||
return new ConversationData(threadId, lastSeen, lastSeenPosition, lastScrolledPosition, jumpToPosition, threadSize, messageRequestData, showUniversalExpireTimerUpdate);
|
||||
return new ConversationData(conversationRecipient, threadId, lastSeen, lastSeenPosition, lastScrolledPosition, jumpToPosition, threadSize, messageRequestData, showUniversalExpireTimerUpdate);
|
||||
}
|
||||
|
||||
public void markGiftBadgeRevealed(long messageId) {
|
||||
|
||||
@@ -186,7 +186,13 @@ public class ConversationViewModel extends ViewModel {
|
||||
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(data.getThreadId(), conversationObserver);
|
||||
ApplicationDependencies.getDatabaseObserver().registerMessageInsertObserver(data.getThreadId(), messageInsertObserver);
|
||||
|
||||
ConversationDataSource dataSource = new ConversationDataSource(context, data.getThreadId(), messageRequestData, data.showUniversalExpireTimerMessage(), data.getThreadSize());
|
||||
ConversationDataSource dataSource = new ConversationDataSource(context,
|
||||
data.getThreadId(),
|
||||
messageRequestData,
|
||||
data.showUniversalExpireTimerMessage(),
|
||||
data.getThreadSize(),
|
||||
data.getThreadRecipient());
|
||||
|
||||
PagingConfig config = new PagingConfig.Builder().setPageSize(25)
|
||||
.setBufferPages(2)
|
||||
.setStartIndex(Math.max(startPosition, 0))
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AttachmentHelper
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
@@ -35,7 +36,7 @@ class ScheduledMessagesRepository {
|
||||
var scheduledMessages: List<MessageRecord> = SignalDatabase.messages.getScheduledMessagesInThread(threadId)
|
||||
val threadRecipient: Recipient = requireNotNull(SignalDatabase.threads.getRecipientForThreadId(threadId))
|
||||
|
||||
val attachmentHelper = ConversationDataSource.AttachmentHelper()
|
||||
val attachmentHelper = AttachmentHelper()
|
||||
|
||||
attachmentHelper.addAll(scheduledMessages)
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import android.app.Application
|
||||
import androidx.annotation.WorkerThread
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.conversation.ConversationDataSource
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AttachmentHelper
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ReactionHelper
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||
@@ -56,8 +57,8 @@ class MessageQuotesRepository {
|
||||
|
||||
var replyRecords: List<MessageRecord> = SignalDatabase.messages.getAllMessagesThatQuote(rootMessageId)
|
||||
|
||||
val reactionHelper = ConversationDataSource.ReactionHelper()
|
||||
val attachmentHelper = ConversationDataSource.AttachmentHelper()
|
||||
val reactionHelper = ReactionHelper()
|
||||
val attachmentHelper = AttachmentHelper()
|
||||
val threadRecipient = requireNotNull(SignalDatabase.threads.getRecipientForThreadId(originalRecord.threadId))
|
||||
|
||||
reactionHelper.addAll(replyRecords)
|
||||
@@ -84,7 +85,7 @@ class MessageQuotesRepository {
|
||||
originalRecord = SignalDatabase.payments.updateMessageWithPayment(originalRecord)
|
||||
}
|
||||
|
||||
originalRecord = ConversationDataSource.ReactionHelper()
|
||||
originalRecord = ReactionHelper()
|
||||
.apply {
|
||||
add(originalRecord)
|
||||
fetchReactions()
|
||||
@@ -92,7 +93,7 @@ class MessageQuotesRepository {
|
||||
.buildUpdatedModels(listOf(originalRecord))
|
||||
.get(0)
|
||||
|
||||
originalRecord = ConversationDataSource.AttachmentHelper()
|
||||
originalRecord = AttachmentHelper()
|
||||
.apply {
|
||||
add(originalRecord)
|
||||
fetchAttachments()
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.thoughtcrime.securesms.conversation.ui.edit
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.thoughtcrime.securesms.conversation.ConversationDataSource
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.AttachmentHelper
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
@@ -36,7 +36,7 @@ object EditMessageHistoryRepository {
|
||||
.getMessageEditHistory(messageId)
|
||||
.toList()
|
||||
|
||||
val attachmentHelper = ConversationDataSource.AttachmentHelper()
|
||||
val attachmentHelper = AttachmentHelper()
|
||||
.apply {
|
||||
addAll(records)
|
||||
fetchAttachments()
|
||||
|
||||
@@ -9,12 +9,11 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.signal.paging.PagedData
|
||||
import org.signal.paging.PagingConfig
|
||||
import org.thoughtcrime.securesms.conversation.ConversationDataSource
|
||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationDataSource
|
||||
import org.thoughtcrime.securesms.database.RxDatabaseObserver
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.threads
|
||||
import org.thoughtcrime.securesms.database.model.Quote
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
@@ -54,7 +53,7 @@ class ConversationRepository(context: Context) {
|
||||
fun getConversationThreadState(threadId: Long, requestedStartPosition: Int): Single<ConversationThreadState> {
|
||||
return Single.fromCallable {
|
||||
SignalLocalMetrics.ConversationOpen.onMetadataLoadStarted()
|
||||
val recipient = threads.getRecipientForThreadId(threadId)!!
|
||||
val recipient = SignalDatabase.threads.getRecipientForThreadId(threadId)!!
|
||||
val metadata = oldConversationRepository.getConversationData(threadId, recipient, requestedStartPosition)
|
||||
val messageRequestData = metadata.messageRequestData
|
||||
val dataSource = ConversationDataSource(
|
||||
@@ -98,7 +97,7 @@ class ConversationRepository(context: Context) {
|
||||
}
|
||||
|
||||
fun setLastVisibleMessageTimestamp(threadId: Long, lastVisibleMessageTimestamp: Long) {
|
||||
SignalExecutors.BOUNDED.submit { threads.setLastScrolled(threadId, lastVisibleMessageTimestamp) }
|
||||
SignalExecutors.BOUNDED.submit { SignalDatabase.threads.setLastScrolled(threadId, lastVisibleMessageTimestamp) }
|
||||
}
|
||||
|
||||
fun markGiftBadgeRevealed(messageId: Long) {
|
||||
@@ -130,7 +129,7 @@ class ConversationRepository(context: Context) {
|
||||
}
|
||||
|
||||
private fun getUnreadCount(threadId: Long): Int {
|
||||
val threadRecord = threads.getThreadRecord(threadId)
|
||||
val threadRecord = SignalDatabase.threads.getThreadRecord(threadId)
|
||||
return threadRecord?.unreadCount ?: 0
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AttachmentHelper {
|
||||
|
||||
private Collection<Long> messageIds = new LinkedList<>();
|
||||
private Map<Long, List<DatabaseAttachment>> messageIdToAttachments = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord record) {
|
||||
if (record.isMms()) {
|
||||
messageIds.add(record.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void addAll(List<MessageRecord> records) {
|
||||
for (MessageRecord record : records) {
|
||||
add(record);
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchAttachments() {
|
||||
messageIdToAttachments = SignalDatabase.attachments().getAttachmentsForMessages(messageIds);
|
||||
}
|
||||
|
||||
public @NonNull List<MessageRecord> buildUpdatedModels(@NonNull Context context, @NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
if (record instanceof MediaMmsMessageRecord) {
|
||||
List<DatabaseAttachment> attachments = messageIdToAttachments.get(record.getId());
|
||||
|
||||
if (Util.hasItems(attachments)) {
|
||||
return ((MediaMmsMessageRecord) record).withAttachments(context, attachments);
|
||||
}
|
||||
}
|
||||
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.CallTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CallHelper {
|
||||
private final Collection<Long> messageIds = new LinkedList<>();
|
||||
private Map<Long, CallTable.Call> messageIdToCall = Collections.emptyMap();
|
||||
|
||||
public void add(MessageRecord messageRecord) {
|
||||
if (messageRecord.isCallLog() && !messageRecord.isGroupCall()) {
|
||||
messageIds.add(messageRecord.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchCalls() {
|
||||
if (!messageIds.isEmpty()) {
|
||||
messageIdToCall = SignalDatabase.calls().getCalls(messageIds);
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull List<MessageRecord> buildUpdatedModels(@NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
if (record.isCallLog() && record instanceof MediaMmsMessageRecord) {
|
||||
CallTable.Call call = messageIdToCall.get(record.getId());
|
||||
if (call != null) {
|
||||
return ((MediaMmsMessageRecord) record).withCall(call);
|
||||
}
|
||||
}
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data
|
||||
|
||||
import android.content.Context
|
||||
import org.signal.core.util.Stopwatch
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.toInt
|
||||
import org.signal.paging.PagedDataSource
|
||||
import org.thoughtcrime.securesms.conversation.ConversationData
|
||||
import org.thoughtcrime.securesms.conversation.ConversationDataSource
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory
|
||||
import org.thoughtcrime.securesms.database.MessageTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord.NoGroupsInCommon
|
||||
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord.RemovedContactHidden
|
||||
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord.UniversalExpireTimerUpdate
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
|
||||
/**
|
||||
* ConversationDataSource for V2. Assumes that ThreadId is never -1L.
|
||||
*/
|
||||
class ConversationDataSource(
|
||||
private val context: Context,
|
||||
private val threadId: Long,
|
||||
private val messageRequestData: ConversationData.MessageRequestData,
|
||||
private val showUniversalExpireTimerUpdate: Boolean,
|
||||
private var baseSize: Int
|
||||
) : PagedDataSource<MessageId, ConversationMessage> {
|
||||
|
||||
init {
|
||||
check(threadId > 0)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ConversationDataSource::class.java)
|
||||
}
|
||||
|
||||
private val threadRecipient: Recipient by lazy {
|
||||
SignalDatabase.threads.getRecipientForThreadId(threadId)!!
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
val startTime = System.currentTimeMillis()
|
||||
val size: Int = getSizeInternal() +
|
||||
messageRequestData.includeWarningUpdateMessage().toInt() +
|
||||
messageRequestData.isHidden.toInt() +
|
||||
showUniversalExpireTimerUpdate.toInt()
|
||||
|
||||
Log.d(TAG, "[size(), thread $threadId] ${System.currentTimeMillis() - startTime} ms")
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
private fun getSizeInternal(): Int {
|
||||
synchronized(this) {
|
||||
if (baseSize != -1) {
|
||||
val size = baseSize
|
||||
baseSize = -1
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
return SignalDatabase.messages.getMessageCountForThread(threadId)
|
||||
}
|
||||
|
||||
override fun load(start: Int, length: Int, cancellationSignal: PagedDataSource.CancellationSignal): List<ConversationMessage> {
|
||||
val stopwatch = Stopwatch("load($start, $length), thread $threadId")
|
||||
var records: MutableList<MessageRecord> = ArrayList(length)
|
||||
val mentionHelper = MentionHelper()
|
||||
val quotedHelper = QuotedHelper()
|
||||
val attachmentHelper = AttachmentHelper()
|
||||
val reactionHelper = ReactionHelper()
|
||||
val paymentHelper = PaymentHelper()
|
||||
val callHelper = CallHelper()
|
||||
val referencedIds = hashSetOf<ServiceId>()
|
||||
|
||||
MessageTable.mmsReaderFor(SignalDatabase.messages.getConversation(threadId, start.toLong(), length.toLong())).forEach { record ->
|
||||
if (cancellationSignal.isCanceled) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
records.add(record)
|
||||
mentionHelper.add(record)
|
||||
quotedHelper.add(record)
|
||||
reactionHelper.add(record)
|
||||
attachmentHelper.add(record)
|
||||
paymentHelper.add(record)
|
||||
callHelper.add(record)
|
||||
|
||||
val updateDescription = record.getUpdateDisplayBody(context, null)
|
||||
if (updateDescription != null) {
|
||||
referencedIds.addAll(updateDescription.mentioned)
|
||||
}
|
||||
}
|
||||
|
||||
if (messageRequestData.includeWarningUpdateMessage() && (start + length >= size())) {
|
||||
records.add(NoGroupsInCommon(threadId, messageRequestData.isGroup))
|
||||
}
|
||||
|
||||
if (messageRequestData.isHidden && (start + length >= size())) {
|
||||
records.add(RemovedContactHidden(threadId))
|
||||
}
|
||||
|
||||
if (showUniversalExpireTimerUpdate) {
|
||||
records.add(UniversalExpireTimerUpdate(threadId))
|
||||
}
|
||||
|
||||
stopwatch.split("messages")
|
||||
|
||||
mentionHelper.fetchMentions(context)
|
||||
stopwatch.split("mentions")
|
||||
|
||||
quotedHelper.fetchQuotedState()
|
||||
stopwatch.split("is-quoted")
|
||||
|
||||
reactionHelper.fetchReactions()
|
||||
stopwatch.split("reactions")
|
||||
|
||||
records = reactionHelper.buildUpdatedModels(records)
|
||||
stopwatch.split("reaction-models")
|
||||
|
||||
attachmentHelper.fetchAttachments()
|
||||
stopwatch.split("attachments")
|
||||
|
||||
records = attachmentHelper.buildUpdatedModels(context, records)
|
||||
stopwatch.split("attachment-models")
|
||||
|
||||
paymentHelper.fetchPayments()
|
||||
stopwatch.split("payments")
|
||||
|
||||
records = paymentHelper.buildUpdatedModels(records)
|
||||
stopwatch.split("payment-models")
|
||||
|
||||
callHelper.fetchCalls()
|
||||
stopwatch.split("calls")
|
||||
|
||||
records = callHelper.buildUpdatedModels(records)
|
||||
stopwatch.split("call-models")
|
||||
|
||||
referencedIds.forEach { Recipient.resolved(RecipientId.from(it)) }
|
||||
stopwatch.split("recipient-resolves")
|
||||
|
||||
val messages = records.map { m ->
|
||||
ConversationMessageFactory.createWithUnresolvedData(
|
||||
context,
|
||||
m,
|
||||
m.getDisplayBody(context),
|
||||
mentionHelper.getMentions(m.id),
|
||||
quotedHelper.isQuoted(m.id),
|
||||
threadRecipient
|
||||
)
|
||||
}
|
||||
|
||||
stopwatch.split("conversion")
|
||||
stopwatch.stop(TAG)
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
override fun load(messageId: MessageId): ConversationMessage? {
|
||||
val stopwatch = Stopwatch("load($messageId), thread $threadId")
|
||||
var record = SignalDatabase.messages.getMessageRecordOrNull(messageId.id)
|
||||
|
||||
if ((record as? MediaMmsMessageRecord)?.parentStoryId?.isGroupReply() == true) {
|
||||
return null
|
||||
}
|
||||
|
||||
val scheduleDate = (record as? MediaMmsMessageRecord)?.scheduledDate
|
||||
if (scheduleDate != null && scheduleDate != -1L) {
|
||||
return null
|
||||
}
|
||||
|
||||
stopwatch.split("message")
|
||||
|
||||
try {
|
||||
if (record == null) {
|
||||
return null
|
||||
} else {
|
||||
val mentions = SignalDatabase.mentions.getMentionsForMessage(messageId.id)
|
||||
stopwatch.split("mentions")
|
||||
|
||||
val isQuoted = SignalDatabase.messages.isQuoted(record)
|
||||
stopwatch.split("is-quoted")
|
||||
|
||||
val reactions = SignalDatabase.reactions.getReactions(messageId)
|
||||
record = ReactionHelper.recordWithReactions(record, reactions)
|
||||
stopwatch.split("reactions")
|
||||
|
||||
val attachments = SignalDatabase.attachments.getAttachmentsForMessage(messageId.id)
|
||||
if (attachments.size > 0) {
|
||||
record = (record as MediaMmsMessageRecord).withAttachments(context, attachments)
|
||||
}
|
||||
stopwatch.split("attachments")
|
||||
|
||||
if (record.isPaymentNotification) {
|
||||
record = SignalDatabase.payments.updateMessageWithPayment(record)
|
||||
}
|
||||
stopwatch.split("payments")
|
||||
|
||||
if (record.isCallLog && !record.isGroupCall) {
|
||||
val call = SignalDatabase.calls.getCallByMessageId(record.id)
|
||||
if (call != null && record is MediaMmsMessageRecord) {
|
||||
record = record.withCall(call)
|
||||
}
|
||||
}
|
||||
stopwatch.split("calls")
|
||||
|
||||
return ConversationMessageFactory.createWithUnresolvedData(
|
||||
ApplicationDependencies.getApplication(),
|
||||
record,
|
||||
record.getDisplayBody(ApplicationDependencies.getApplication()),
|
||||
mentions,
|
||||
isQuoted,
|
||||
threadRecipient
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
stopwatch.stop(TAG)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getKey(conversationMessage: ConversationMessage): MessageId {
|
||||
return MessageId(conversationMessage.messageRecord.id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MentionHelper {
|
||||
|
||||
private Collection<Long> messageIds = new LinkedList<>();
|
||||
private Map<Long, List<Mention>> messageIdToMentions = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord record) {
|
||||
if (record.isMms()) {
|
||||
messageIds.add(record.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchMentions(Context context) {
|
||||
messageIdToMentions = SignalDatabase.mentions().getMentionsForMessages(messageIds);
|
||||
}
|
||||
|
||||
public @Nullable List<Mention> getMentions(long id) {
|
||||
return messageIdToMentions.get(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.payments.Payment;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaymentHelper {
|
||||
private final Map<UUID, Long> paymentMessages = new HashMap<>();
|
||||
private final Map<Long, Payment> messageIdToPayment = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord messageRecord) {
|
||||
if (messageRecord.isMms() && messageRecord.isPaymentNotification()) {
|
||||
UUID paymentUuid = UuidUtil.parseOrNull(messageRecord.getBody());
|
||||
if (paymentUuid != null) {
|
||||
paymentMessages.put(paymentUuid, messageRecord.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchPayments() {
|
||||
List<Payment> payments = SignalDatabase.payments().getPayments(paymentMessages.keySet());
|
||||
for (Payment payment : payments) {
|
||||
if (payment != null) {
|
||||
messageIdToPayment.put(paymentMessages.get(payment.getUuid()), payment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull List<MessageRecord> buildUpdatedModels(@NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
if (record instanceof MediaMmsMessageRecord) {
|
||||
Payment payment = messageIdToPayment.get(record.getId());
|
||||
if (payment != null) {
|
||||
return ((MediaMmsMessageRecord) record).withPayment(payment);
|
||||
}
|
||||
}
|
||||
return record;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data;
|
||||
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
|
||||
public class QuotedHelper {
|
||||
|
||||
private Collection<MessageRecord> records = new LinkedList<>();
|
||||
private Set<Long> hasBeenQuotedIds = new HashSet<>();
|
||||
|
||||
public void add(MessageRecord record) {
|
||||
records.add(record);
|
||||
}
|
||||
|
||||
public void fetchQuotedState() {
|
||||
hasBeenQuotedIds = SignalDatabase.messages().isQuoted(records);
|
||||
}
|
||||
|
||||
public boolean isQuoted(long id) {
|
||||
return hasBeenQuotedIds.contains(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.thoughtcrime.securesms.conversation.v2.data;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageId;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ReactionHelper {
|
||||
|
||||
private Collection<MessageId> messageIds = new LinkedList<>();
|
||||
private Map<MessageId, List<ReactionRecord>> messageIdToReactions = new HashMap<>();
|
||||
|
||||
public void add(MessageRecord record) {
|
||||
messageIds.add(new MessageId(record.getId()));
|
||||
}
|
||||
|
||||
public void addAll(List<MessageRecord> records) {
|
||||
for (MessageRecord record : records) {
|
||||
add(record);
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchReactions() {
|
||||
messageIdToReactions = SignalDatabase.reactions().getReactionsForMessages(messageIds);
|
||||
}
|
||||
|
||||
public @NonNull List<MessageRecord> buildUpdatedModels(@NonNull List<MessageRecord> records) {
|
||||
return records.stream()
|
||||
.map(record -> {
|
||||
MessageId messageId = new MessageId(record.getId());
|
||||
List<ReactionRecord> reactions = messageIdToReactions.get(messageId);
|
||||
|
||||
return recordWithReactions(record, reactions);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static @NonNull MessageRecord recordWithReactions(@NonNull MessageRecord record, List<ReactionRecord> reactions) {
|
||||
if (Util.hasItems(reactions)) {
|
||||
if (record instanceof MediaMmsMessageRecord) {
|
||||
return ((MediaMmsMessageRecord) record).withReactions(reactions);
|
||||
} else {
|
||||
throw new IllegalStateException("We have reactions for an unsupported record type: " + record.getClass().getName());
|
||||
}
|
||||
} else {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user