Improved send performance.

This commit is contained in:
Greyson Parrelli
2026-05-24 08:55:15 -04:00
committed by Michelle Tang
parent b2f450d849
commit 5d45914a08
26 changed files with 299 additions and 133 deletions
@@ -6,7 +6,7 @@ import androidx.lifecycle.LifecycleOwner;
import com.bumptech.glide.RequestManager;
import org.thoughtcrime.securesms.conversationlist.model.ConversationSet;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import java.util.Locale;
import java.util.Set;
@@ -14,7 +14,7 @@ import java.util.Set;
public interface BindableConversationListItem extends Unbindable {
void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager, @NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull ConversationSet selectedConversations,
@@ -61,7 +61,7 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData
expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME).seconds.inWholeMilliseconds.takeIf { it > 0 },
expireTimerVersion = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION),
muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL).takeIf { it > 0 },
markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD,
markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.ForcedUnread,
dontNotifyForMentionsIfMuted = RecipientTable.NotificationSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING),
style = ChatStyleConverter.constructRemoteChatStyle(
db = db,
@@ -46,7 +46,7 @@ object ChatArchiveImporter {
ThreadTable.RECIPIENT_ID to recipientId.serialize(),
ThreadTable.PINNED_ORDER to chat.pinnedOrder,
ThreadTable.ARCHIVED to chat.archived.toInt(),
ThreadTable.READ to if (chat.markedUnread) ThreadTable.ReadStatus.FORCED_UNREAD.serialize() else ThreadTable.ReadStatus.READ.serialize(),
ThreadTable.READ to if (chat.markedUnread) ThreadTable.ReadStatus.ForcedUnread.serialize() else ThreadTable.ReadStatus.Read.serialize(),
ThreadTable.ACTIVE to 1
)
.run()
@@ -54,7 +54,7 @@ import org.signal.core.ui.compose.DayNightPreviews
import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Rows
import org.signal.core.ui.compose.SignalIcons
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
class DataSeedingPlaygroundFragment : ComposeFragment() {
@@ -285,7 +285,7 @@ fun Screen(
@Composable
private fun ThreadSelectionRow(
thread: ThreadRecord,
thread: ThreadWithRecipient,
isSelected: Boolean,
onSelectionChanged: (Boolean) -> Unit
) {
@@ -25,7 +25,7 @@ import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.attachments.UriAttachment
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.MediaUtil
@@ -43,7 +43,7 @@ class DataSeedingPlaygroundViewModel(application: Application) : AndroidViewMode
fun loadThreads() {
viewModelScope.launch(Dispatchers.IO) {
try {
val threads = mutableListOf<ThreadRecord>()
val threads = mutableListOf<ThreadWithRecipient>()
val cursor: Cursor = SignalDatabase.threads.getRecentConversationList(
limit = MAX_RECENT_THREADS,
includeInactiveGroups = false,
@@ -213,7 +213,7 @@ class DataSeedingPlaygroundViewModel(application: Application) : AndroidViewMode
}
data class DataSeedingPlaygroundState(
val threads: List<ThreadRecord> = emptyList(),
val threads: List<ThreadWithRecipient> = emptyList(),
val selectedThreads: Set<Long> = emptySet(),
val mediaFiles: List<String> = emptyList(),
val selectedFolderPath: String = ""
@@ -5,7 +5,7 @@ import androidx.annotation.VisibleForTesting
import org.thoughtcrime.securesms.contacts.HeaderAction
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.groups.GroupsInCommonSummary
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.search.MessageResult
@@ -49,8 +49,8 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
*/
data class Thread(
val query: String,
val threadRecord: ThreadRecord
) : ContactSearchData(ContactSearchKey.Thread(threadRecord.threadId))
val threadWithRecipient: ThreadWithRecipient
) : ContactSearchData(ContactSearchKey.Thread(threadWithRecipient.threadId))
/**
* A row displaying a group which has members that match the given query.
@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.keyvalue.StorySend
import org.thoughtcrime.securesms.phonenumbers.NumberUtil
import org.thoughtcrime.securesms.recipients.Recipient
@@ -476,7 +476,7 @@ class ContactSearchPagedDataSource(
}
}
private fun getThreadData(query: String?, unreadOnly: Boolean): ContactSearchIterator<ThreadRecord> {
private fun getThreadData(query: String?, unreadOnly: Boolean): ContactSearchIterator<ThreadWithRecipient> {
check(searchRepository != null)
if (searchCache.threadSearchResult == null && query != null) {
searchCache = searchCache.copy(threadSearchResult = searchRepository.queryThreadsSync(query, unreadOnly))
@@ -19,7 +19,7 @@ import org.thoughtcrime.securesms.database.MessageTypes;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.database.model.UpdateDescription;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -79,7 +79,7 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
Set<RecipientId> needsResolve = new HashSet<>();
try (ConversationReader reader = new ConversationReader(getCursor(start, length))) {
ThreadRecord record;
ThreadWithRecipient record;
while ((record = reader.getNext()) != null && !cancellationSignal.isCanceled()) {
conversations.add(new Conversation(record));
recipients.add(record.getRecipient());
@@ -135,7 +135,7 @@ import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter;
import org.thoughtcrime.securesms.database.MessageTable.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
@@ -668,7 +668,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
}
}
private void onConversationClicked(@NonNull ThreadRecord threadRecord) {
private void onConversationClicked(@NonNull ThreadWithRecipient threadRecord) {
hideKeyboard();
getNavigator().goToConversation(threadRecord.getRecipient().getId(),
threadRecord.getThreadId(),
@@ -1843,7 +1843,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
}
private void onTrueSwipe(RecyclerView.ViewHolder viewHolder) {
ThreadRecord thread = ((ConversationListItem) viewHolder.itemView).getThread();
ThreadWithRecipient thread = ((ConversationListItem) viewHolder.itemView).getThread();
onItemSwiped(thread.getThreadId(), thread.getUnreadCount(), thread.getUnreadSelfMentionsCount());
}
@@ -1966,12 +1966,12 @@ public class ConversationListFragment extends MainFragment implements Conversati
@Override
public void onThreadClicked(@NonNull View view, @NonNull ContactSearchData.Thread thread, boolean isSelected) {
onConversationClicked(thread.getThreadRecord());
onConversationClicked(thread.getThreadWithRecipient());
}
@Override
public boolean onThreadLongClicked(@NonNull View view, @NonNull ContactSearchData.Thread thread) {
return showConversationContextMenu(new Conversation(thread.getThreadRecord()), view, true);
return showConversationContextMenu(new Conversation(thread.getThreadWithRecipient()), view, true);
}
@Override
@@ -70,7 +70,7 @@ import org.thoughtcrime.securesms.database.MessageTypes;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.database.model.UpdateDescription;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.thoughtcrime.securesms.glide.targets.GlideLiveDataTarget;
@@ -126,7 +126,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
private AlertView alertView;
private TextView unreadIndicator;
private long lastSeen;
private ThreadRecord thread;
private ThreadWithRecipient thread;
private boolean batchMode;
private Locale locale;
private String highlightSubstring;
@@ -209,7 +209,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
@Override
public void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager glideRequests,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@@ -220,7 +220,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
public void bindThread(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@@ -467,7 +467,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
return threadId;
}
public @NonNull ThreadRecord getThread() {
public @NonNull ThreadWithRecipient getThread() {
return thread;
}
@@ -516,7 +516,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
}
private void setStatusIcons(ThreadRecord thread) {
private void setStatusIcons(ThreadWithRecipient thread) {
if (MessageTypes.isBadDecryptType(thread.getType())) {
deliveryStatusIndicator.setNone();
alertView.setFailed();
@@ -556,7 +556,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
}
private void setUnreadIndicator(ThreadRecord thread) {
private void setUnreadIndicator(ThreadWithRecipient thread) {
if (thread.isRead()) {
unreadIndicator.setVisibility(View.GONE);
unreadMentions.setVisibility(View.GONE);
@@ -596,7 +596,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
private static @NonNull LiveData<SpannableString> getThreadDisplayBody(@NonNull Context context,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@Px int thumbSize,
@NonNull GlideLiveDataTarget thumbTarget)
@@ -734,7 +734,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
private static LiveData<CharSequence> createFinalBodyWithMediaIcon(@NonNull Context context,
@NonNull CharSequence body,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@Px int thumbSize,
@NonNull GlideLiveDataTarget thumbTarget)
@@ -13,7 +13,7 @@ import com.bumptech.glide.RequestManager;
import org.thoughtcrime.securesms.BindableConversationListItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversationlist.model.ConversationSet;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import java.util.Locale;
import java.util.Set;
@@ -42,7 +42,7 @@ public class ConversationListItemAction extends FrameLayout implements BindableC
@Override
public void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@@ -221,7 +221,7 @@ object ConversationListSearchModels {
(itemView as ConversationListItem).bindThread(
lifecycleOwner,
model.thread.threadRecord,
model.thread.threadWithRecipient,
requestManager,
Locale.getDefault(),
emptySet(),
@@ -2,13 +2,13 @@ package org.thoughtcrime.securesms.conversationlist.model;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
public class Conversation {
private final ThreadRecord threadRecord;
private final Type type;
private final ThreadWithRecipient threadRecord;
private final Type type;
public Conversation(@NonNull ThreadRecord threadRecord) {
public Conversation(@NonNull ThreadWithRecipient threadRecord) {
this.threadRecord = threadRecord;
if (this.threadRecord.getThreadId() < 0) {
type = Type.valueOf(this.threadRecord.getBody());
@@ -17,7 +17,7 @@ public class Conversation {
}
}
public @NonNull ThreadRecord getThreadRecord() {
public @NonNull ThreadWithRecipient getThreadRecord() {
return threadRecord;
}
@@ -5,7 +5,7 @@ import android.database.Cursor;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.signal.core.util.CursorUtil;
@@ -37,7 +37,7 @@ public class ConversationReader extends ThreadTable.StaticReader {
}
@Override
public ThreadRecord getCurrent() {
public ThreadWithRecipient getCurrent() {
if (cursor.getColumnIndex(HEADER_COLUMN[0]) == -1) {
return super.getCurrent();
} else {
@@ -45,7 +45,7 @@ public class ConversationReader extends ThreadTable.StaticReader {
}
}
private ThreadRecord buildThreadRecordForHeader() {
private ThreadWithRecipient buildThreadRecordForHeader() {
Conversation.Type type = Conversation.Type.valueOf(CursorUtil.requireString(cursor, HEADER_COLUMN[0]));
int count = 0;
if (type == Conversation.Type.ARCHIVED_FOOTER) {
@@ -60,8 +60,8 @@ public class ConversationReader extends ThreadTable.StaticReader {
return buildThreadRecordForType(type, count, showTip);
}
public static ThreadRecord buildThreadRecordForType(@NonNull Conversation.Type type, int count, boolean showTip) {
return new ThreadRecord.Builder(-(100 + type.ordinal()))
public static ThreadWithRecipient buildThreadRecordForType(@NonNull Conversation.Type type, int count, boolean showTip) {
return new ThreadWithRecipient.Builder(-(100 + type.ordinal()))
.setBody(type.toString())
.setDate(100)
.setRecipient(Recipient.UNKNOWN)
@@ -311,6 +311,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
private const val INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID = "message_thread_story_parent_story_scheduled_date_latest_revision_id_index"
private const val INDEX_THREAD_DATE_RECEIVED_UNREAD = "message_thread_date_received_unread_index"
private const val INDEX_COLLAPSED_STATE = "message_collapsed_state_index"
private const val INDEX_DATE_SENT_FROM_TO_THREAD = "message_date_sent_from_to_thread_index"
private const val INDEX_THREAD_COUNT = "message_thread_count_index"
private const val INDEX_THREAD_UNREAD_COUNT = "message_thread_unread_count_index"
@@ -4657,10 +4658,22 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun setReactionsSeen(threadId: Long, sinceTimestamp: Long) {
val where = "$THREAD_ID = ? AND $REACTIONS_UNREAD = ?" + if (sinceTimestamp > -1) " AND $DATE_RECEIVED <= $sinceTimestamp" else ""
// The $STORY_TYPE/$PARENT_STORY_ID and "(read=0 OR reactions_unread=1 OR votes_unread=1)" predicates are required for the
// query planner to recognize that $INDEX_THREAD_DATE_RECEIVED_UNREAD (a partial index) covers this query.
// They match exactly the WHERE clause used when defining that index.
var where = """
$THREAD_ID = ? AND
$STORY_TYPE = 0 AND
$PARENT_STORY_ID <= 0 AND
($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1) AND
$REACTIONS_UNREAD = ?
"""
if (sinceTimestamp > -1) {
where += " AND $DATE_RECEIVED <= $sinceTimestamp"
}
writableDatabase
.update(TABLE_NAME)
.update("$TABLE_NAME INDEXED BY $INDEX_THREAD_DATE_RECEIVED_UNREAD")
.values(
REACTIONS_UNREAD to 0,
REACTIONS_LAST_SEEN to System.currentTimeMillis()
@@ -4681,14 +4694,20 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun setVoteSeen(threadId: Long, sinceTimestamp: Long) {
val where = if (sinceTimestamp > -1) {
"$THREAD_ID = ? AND $VOTES_UNREAD = ? AND $DATE_RECEIVED <= $sinceTimestamp"
} else {
"$THREAD_ID = ? AND $VOTES_UNREAD = ?"
// See setReactionsSeen for an explanation of the extra predicates / INDEXED BY hint.
var where = """
$THREAD_ID = ? AND
$STORY_TYPE = 0 AND
$PARENT_STORY_ID <= 0 AND
($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1) AND
$VOTES_UNREAD = ?
"""
if (sinceTimestamp > -1) {
where += " AND $DATE_RECEIVED <= $sinceTimestamp"
}
writableDatabase
.update(TABLE_NAME)
.update("$TABLE_NAME INDEXED BY $INDEX_THREAD_DATE_RECEIVED_UNREAD")
.values(
VOTES_UNREAD to 0,
VOTES_LAST_SEEN to System.currentTimeMillis()
@@ -4709,6 +4728,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun collapsePendingCollapsibleEvents(threadId: Long, sinceTimestamp: Long) {
// Force INDEXED BY message_collapsed_state_index. COLLAPSED_STATE = PENDING_COLLAPSED is a transient state, so the index
// entries for it are typically near zero — much more selective than scanning the thread by date_received.
val where = if (sinceTimestamp > -1) {
"$THREAD_ID = ? AND $COLLAPSED_STATE = ? AND $DATE_RECEIVED <= $sinceTimestamp"
} else {
@@ -4716,7 +4737,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
writableDatabase
.update(TABLE_NAME)
.update("$TABLE_NAME INDEXED BY $INDEX_COLLAPSED_STATE")
.values(COLLAPSED_STATE to CollapsedState.COLLAPSED.id)
.where(where, threadId, CollapsedState.PENDING_COLLAPSED.id)
.run()
@@ -70,7 +70,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase.Companion.threads
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.KeyTransparencyStore
import org.thoughtcrime.securesms.database.model.RecipientRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.database.model.databaseprotos.BadgeList
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime
import org.thoughtcrime.securesms.database.model.databaseprotos.ExpiringProfileKeyCredentialColumnData
@@ -3809,7 +3809,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
val recipientsWithinInteractionThreshold: MutableSet<RecipientId> = LinkedHashSet()
threadDatabase.readerFor(threadDatabase.getRecentPushConversationList(-1)).use { reader ->
var record: ThreadRecord? = reader.getNext()
var record: ThreadWithRecipient? = reader.getNext()
while (record != null && record.date > lastInteractionThreshold) {
val recipient = Recipient.resolved(record.recipient.id)
@@ -209,7 +209,7 @@ object RecipientTableCursorUtil {
identityKey = cursor.optionalString(RecipientTable.IDENTITY_KEY).map { Base64.decodeOrThrow(it) }.orElse(null),
identityStatus = cursor.optionalInt(RecipientTable.IDENTITY_STATUS).map { VerifiedStatus.forState(it) }.orElse(VerifiedStatus.DEFAULT),
isArchived = cursor.optionalBoolean(ThreadTable.ARCHIVED).orElse(false),
isForcedUnread = cursor.optionalInt(ThreadTable.READ).map { status: Int -> status == ThreadTable.ReadStatus.FORCED_UNREAD.serialize() }.orElse(false),
isForcedUnread = cursor.optionalInt(ThreadTable.READ).map { status: Int -> status == ThreadTable.ReadStatus.ForcedUnread.serialize() }.orElse(false),
unregisteredTimestamp = cursor.optionalLong(RecipientTable.UNREGISTERED_TIMESTAMP).orElse(0),
systemNickname = cursor.optionalString(RecipientTable.SYSTEM_NICKNAME).orElse(null),
pniSignatureVerified = cursor.optionalBoolean(RecipientTable.PNI_SIGNATURE_VERIFIED).orElse(false)
@@ -26,9 +26,12 @@ import org.signal.core.util.readToSingleBoolean
import org.signal.core.util.readToSingleInt
import org.signal.core.util.readToSingleIntOrNull
import org.signal.core.util.readToSingleLong
import org.signal.core.util.readToSingleObject
import org.signal.core.util.requireBlob
import org.signal.core.util.requireBoolean
import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
import org.signal.core.util.requireLongOrNull
import org.signal.core.util.requireString
import org.signal.core.util.select
import org.signal.core.util.toInt
@@ -51,6 +54,7 @@ import org.thoughtcrime.securesms.database.ThreadBodyUtil.ThreadBody
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras
import org.thoughtcrime.securesms.database.model.serialize
@@ -131,7 +135,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
$DATE INTEGER DEFAULT 0,
$MEANINGFUL_MESSAGES INTEGER DEFAULT 0,
$RECIPIENT_ID INTEGER NOT NULL UNIQUE REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}) ON DELETE CASCADE,
$READ INTEGER DEFAULT ${ReadStatus.READ.serialize()},
$READ INTEGER DEFAULT ${ReadStatus.Read.serialize()},
$TYPE INTEGER DEFAULT 0,
$ERROR INTEGER DEFAULT 0,
$SNIPPET TEXT,
@@ -481,7 +485,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
writableDatabase
.updateAll(TABLE_NAME)
.values(
READ to ReadStatus.READ.serialize(),
READ to ReadStatus.Read.serialize(),
UNREAD_COUNT to 0,
UNREAD_SELF_MENTION_COUNT to 0
)
@@ -565,7 +569,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
writableDatabase.withinTransaction { db ->
for ((threadId, sinceTimestamp) in threadIdToSinceTimestamp) {
val previous = getThreadRecord(threadId)
val previous = getRecipientIdAndRead(threadId)
messageRecords += messages.setMessagesReadSince(threadId, sinceTimestamp)
@@ -578,7 +582,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val lastSeenTimestamp = messages.getMostRecentReadMessageDateReceived(threadId) ?: System.currentTimeMillis()
val contentValues = contentValuesOf(
READ to ReadStatus.READ.serialize(),
READ to ReadStatus.Read.serialize(),
UNREAD_COUNT to unreadCount,
UNREAD_SELF_MENTION_COUNT to unreadMentionsCount,
LAST_SEEN to lastSeenTimestamp
@@ -589,8 +593,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
.where("$ID = ?", threadId)
.run()
if (previous != null && previous.isForcedUnread) {
recipients.markNeedsSync(previous.recipient.id)
if (previous != null && previous.read == ReadStatus.ForcedUnread) {
recipients.markNeedsSync(previous.recipientId)
needsSync = true
}
}
@@ -611,7 +615,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
writableDatabase.withinTransaction { db ->
val query = SqlUtil.buildSingleCollectionQuery(ID, threadIds)
val contentValues = contentValuesOf(READ to ReadStatus.FORCED_UNREAD.serialize())
val contentValues = contentValuesOf(READ to ReadStatus.ForcedUnread.serialize())
db.update(TABLE_NAME, contentValues, query.where, query.whereArgs)
recipientIds = getRecipientIdsForThreadIds(threadIds)
@@ -657,7 +661,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val forcedUnreadCount: Long = readableDatabase
.select("COUNT(*)")
.from(TABLE_NAME)
.where("$READ = ? AND $ARCHIVED = ?", ReadStatus.FORCED_UNREAD.serialize(), 0)
.where("$READ = ? AND $ARCHIVED = ?", ReadStatus.ForcedUnread.serialize(), 0)
.run()
.use { cursor ->
if (cursor.moveToFirst()) {
@@ -737,7 +741,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON $TABLE_NAME.$RECIPIENT_ID = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID}
WHERE
$ARCHIVED = 0 AND
$READ = ${ThreadTable.ReadStatus.FORCED_UNREAD.serialize()}
$READ = ${ThreadTable.ReadStatus.ForcedUnread.serialize()}
$chatFolderQuery
"""
val forcedUnreadCount = readableDatabase.rawQuery(forcedUnreadCountQuery, null).readToSingleInt(0)
@@ -777,7 +781,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return readableDatabase
.select(*aggregator)
.from(TABLE_NAME)
.where("$READ != ${ReadStatus.READ.serialize()} AND $ARCHIVED = 0 AND $MEANINGFUL_MESSAGES != 0")
.where("$READ != ${ReadStatus.Read.serialize()} AND $ARCHIVED = 0 AND $MEANINGFUL_MESSAGES != 0")
.run()
.use(mapCursorToType)
}
@@ -786,7 +790,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
writableDatabase.execSQL(
"""
UPDATE $TABLE_NAME
SET $READ = ${ReadStatus.UNREAD.serialize()},
SET $READ = ${ReadStatus.Unread.serialize()},
$UNREAD_COUNT = $UNREAD_COUNT + ?,
$UNREAD_SELF_MENTION_COUNT = $UNREAD_SELF_MENTION_COUNT + ?,
$LAST_SCROLLED = ?
@@ -852,7 +856,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
selection += if (unreadOnly) {
") AND $TABLE_NAME.$READ != ${ReadStatus.READ.serialize()}"
") AND $TABLE_NAME.$READ != ${ReadStatus.Read.serialize()}"
} else {
")"
}
@@ -1570,14 +1574,14 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
fun updateReadState(threadId: Long) {
val previous = getThreadRecord(threadId)
val previous = getRecipientIdAndRead(threadId)
val unreadCount = messages.getUnreadCount(threadId)
val unreadMentionsCount = messages.getUnreadMentionCount(threadId)
writableDatabase
.update(TABLE_NAME)
.values(
READ to if (unreadCount == 0) ReadStatus.READ.serialize() else ReadStatus.UNREAD.serialize(),
READ to if (unreadCount == 0) ReadStatus.Read.serialize() else ReadStatus.Unread.serialize(),
UNREAD_COUNT to unreadCount,
UNREAD_SELF_MENTION_COUNT to unreadMentionsCount
)
@@ -1586,8 +1590,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
notifyConversationListListeners()
if (previous != null && previous.isForcedUnread) {
recipients.markNeedsSync(previous.recipient.id)
if (previous != null && previous.read == ReadStatus.ForcedUnread) {
recipients.markNeedsSync(previous.recipientId)
StorageSyncHelper.scheduleSyncForDataChange()
}
}
@@ -1670,12 +1674,12 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val threadId: Long? = if (archived) getOrCreateThreadIdFor(recipientId, isGroup) else getThreadIdFor(recipientId)
if (forcedUnread) {
values.put(READ, ReadStatus.FORCED_UNREAD.serialize())
values.put(READ, ReadStatus.ForcedUnread.serialize())
} else if (threadId != null) {
val unreadCount = messages.getUnreadCount(threadId)
val unreadMentionsCount = messages.getUnreadMentionCount(threadId)
values.put(READ, if (unreadCount == 0) ReadStatus.READ.serialize() else ReadStatus.UNREAD.serialize())
values.put(READ, if (unreadCount == 0) ReadStatus.Read.serialize() else ReadStatus.Unread.serialize())
values.put(UNREAD_COUNT, unreadCount)
values.put(UNREAD_SELF_MENTION_COUNT, unreadMentionsCount)
}
@@ -1898,10 +1902,6 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
.run()
}
fun getThreadRecordFor(recipient: Recipient): ThreadRecord {
return getThreadRecord(getOrCreateThreadIdFor(recipient))!!
}
fun getAllThreadRecipients(): Set<RecipientId> {
return readableDatabase
.select(RECIPIENT_ID)
@@ -1988,14 +1988,101 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return null
}
val query = createQuery("$TABLE_NAME.$ID = ?", 1)
return readableDatabase.rawQuery(query, SqlUtil.buildArgs(threadId)).use { cursor ->
if (cursor.moveToFirst()) {
readerFor(cursor).getCurrent()
} else {
null
return readableDatabase
.select(
ID,
DATE,
MEANINGFUL_MESSAGES,
RECIPIENT_ID,
READ,
TYPE,
ERROR,
SNIPPET,
SNIPPET_TYPE,
SNIPPET_URI,
SNIPPET_CONTENT_TYPE,
SNIPPET_EXTRAS,
UNREAD_COUNT,
ARCHIVED,
STATUS,
HAS_DELIVERY_RECEIPT,
HAS_READ_RECEIPT,
EXPIRES_IN,
LAST_SEEN,
HAS_SENT,
LAST_SCROLLED,
PINNED_ORDER,
UNREAD_SELF_MENTION_COUNT,
ACTIVE,
SNIPPET_MESSAGE_EXTRAS,
SNIPPET_MESSAGE_ID
)
.from(TABLE_NAME)
.where("$ID = ?", threadId)
.run()
.readToSingleObject { cursor ->
ThreadRecord(
threadId = cursor.requireLong(ID),
date = cursor.requireLong(DATE),
meaningfulMessages = cursor.requireBoolean(MEANINGFUL_MESSAGES),
recipientId = RecipientId.from(cursor.requireLong(RECIPIENT_ID)),
read = ReadStatus.deserialize(cursor.requireInt(READ)),
type = cursor.requireLong(TYPE),
error = cursor.requireInt(ERROR),
snippet = cursor.requireString(SNIPPET),
snippetType = cursor.requireLong(SNIPPET_TYPE),
snippetUri = parseSnippetUri(cursor.requireString(SNIPPET_URI)),
snippetContentType = cursor.requireString(SNIPPET_CONTENT_TYPE),
snippetExtras = parseSnippetExtras(cursor.requireString(SNIPPET_EXTRAS)),
unreadCount = cursor.requireInt(UNREAD_COUNT),
archived = cursor.requireBoolean(ARCHIVED),
status = cursor.requireLong(STATUS),
hasDeliveryReceipt = cursor.requireBoolean(HAS_DELIVERY_RECEIPT),
hasReadReceipt = cursor.requireBoolean(HAS_READ_RECEIPT),
expiresIn = cursor.requireLong(EXPIRES_IN),
lastSeen = cursor.requireLong(LAST_SEEN),
hasSent = cursor.requireBoolean(HAS_SENT),
lastScrolled = cursor.requireLong(LAST_SCROLLED),
pinnedOrder = cursor.requireLongOrNull(PINNED_ORDER),
unreadSelfMentionCount = cursor.requireInt(UNREAD_SELF_MENTION_COUNT),
active = cursor.requireBoolean(ACTIVE),
snippetMessageExtras = cursor.requireBlob(SNIPPET_MESSAGE_EXTRAS)?.let { MessageExtras.ADAPTER.decode(it) },
snippetMessageId = cursor.requireLong(SNIPPET_MESSAGE_ID)
)
}
}
private fun parseSnippetUri(uriString: String?): Uri? {
if (uriString == null) return null
return try {
Uri.parse(uriString)
} catch (e: IllegalArgumentException) {
Log.w(TAG, e)
null
}
}
private fun parseSnippetExtras(extraString: String?): Extra? {
if (extraString == null) return null
return try {
val jsonObject = SaneJSONObject(JSONObject(extraString))
Extra(
isViewOnce = jsonObject.getBoolean("isRevealable"),
isSticker = jsonObject.getBoolean("isSticker"),
stickerEmoji = jsonObject.getString("stickerEmoji"),
isAlbum = jsonObject.getBoolean("isAlbum"),
deletedBy = jsonObject.getString("deletedBy"),
isMessageRequestAccepted = jsonObject.getBoolean("isMessageRequestAccepted"),
isGv2Invite = jsonObject.getBoolean("isGv2Invite"),
groupAddedBy = jsonObject.getString("groupAddedBy"),
individualRecipientId = jsonObject.getString("individualRecipientId")!!,
bodyRanges = jsonObject.getString("bodyRanges"),
isScheduled = jsonObject.getBoolean("isScheduled"),
isRecipientHidden = jsonObject.getBoolean("isRecipientHidden"),
isPoll = jsonObject.getBoolean("isPoll")
)
} catch (e: Exception) {
null
}
}
@@ -2016,7 +2103,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val contentValues = contentValuesOf(
DATE to 0,
MEANINGFUL_MESSAGES to 0,
READ to ReadStatus.READ.serialize(),
READ to ReadStatus.Read.serialize(),
TYPE to 0,
ERROR to 0,
SNIPPET to null,
@@ -2196,6 +2283,20 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return query
}
private fun getRecipientIdAndRead(threadId: Long): RecipientIdAndRead? {
return readableDatabase
.select(RECIPIENT_ID, READ)
.from(TABLE_NAME)
.where("$ID = $threadId")
.run()
.readToSingleObject {
RecipientIdAndRead(
recipientId = RecipientId.from(it.requireLong(RECIPIENT_ID)),
read = ReadStatus.deserialize(it.requireInt(READ))
)
}
}
private fun isSilentType(type: Long): Boolean {
return MessageTypes.isProfileChange(type) ||
MessageTypes.isGroupV1MigrationEvent(type) ||
@@ -2235,7 +2336,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
if (this.showUnread) {
fullQuery.add("$UNREAD_COUNT > 0 OR $READ == ${ReadStatus.FORCED_UNREAD.serialize()}")
fullQuery.add("$UNREAD_COUNT > 0 OR $READ == ${ReadStatus.ForcedUnread.serialize()}")
}
if (!this.showMutedChats) {
@@ -2249,7 +2350,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return when (this) {
ConversationFilter.OFF -> ""
//language=sql
ConversationFilter.UNREAD -> " AND ($UNREAD_COUNT > 0 OR $READ == ${ReadStatus.FORCED_UNREAD.serialize()})"
ConversationFilter.UNREAD -> " AND ($UNREAD_COUNT > 0 OR $READ == ${ReadStatus.ForcedUnread.serialize()})"
ConversationFilter.MUTED -> error("This filter selection isn't supported yet.")
ConversationFilter.GROUPS -> error("This filter selection isn't supported yet.")
}
@@ -2266,7 +2367,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
inner class Reader(cursor: Cursor) : StaticReader(cursor, context)
open class StaticReader(private val cursor: Cursor, private val context: Context) : Closeable {
fun getNext(): ThreadRecord? {
fun getNext(): ThreadWithRecipient? {
return if (!cursor.moveToNext()) {
null
} else {
@@ -2274,7 +2375,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
}
open fun getCurrent(): ThreadRecord? {
open fun getCurrent(): ThreadWithRecipient? {
val recipientId = RecipientId.from(cursor.requireLong(RECIPIENT_ID))
val recipientSettings = RecipientTableCursorUtil.getRecord(context, cursor, RECIPIENT_ID)
@@ -2319,7 +2420,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
null
}
return ThreadRecord.Builder(cursor.requireLong(ID))
return ThreadWithRecipient.Builder(cursor.requireLong(ID))
.setRecipient(recipient)
.setType(cursor.requireLong(SNIPPET_TYPE))
.setDistributionType(cursor.requireInt(TYPE))
@@ -2335,7 +2436,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
.setContentType(cursor.requireString(SNIPPET_CONTENT_TYPE))
.setMeaningfulMessages(cursor.requireLong(MEANINGFUL_MESSAGES) > 0)
.setUnreadCount(cursor.requireInt(UNREAD_COUNT))
.setForcedUnread(cursor.requireInt(READ) == ReadStatus.FORCED_UNREAD.serialize())
.setForcedUnread(cursor.requireInt(READ) == ReadStatus.ForcedUnread.serialize())
.setPinned(cursor.requireBoolean(PINNED_ORDER))
.setUnreadSelfMentionsCount(cursor.requireInt(UNREAD_SELF_MENTION_COUNT))
.setExtra(extra)
@@ -2454,10 +2555,10 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
}
internal enum class ReadStatus(private val value: Int) {
READ(1),
UNREAD(0),
FORCED_UNREAD(2);
enum class ReadStatus(private val value: Int) {
Read(1),
Unread(0),
ForcedUnread(2);
fun serialize(): Int {
return value
@@ -2491,4 +2592,9 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
)
data class ThreadDeleteSyncInfo(val threadId: Long, val addressableMessages: Set<MessageRecord>, val nonExpiringAddressableMessages: Set<MessageRecord>)
data class RecipientIdAndRead(
val recipientId: RecipientId,
val read: ReadStatus
)
}
@@ -0,0 +1,42 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database.model
import android.net.Uri
import org.thoughtcrime.securesms.database.ThreadTable
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras
import org.thoughtcrime.securesms.recipients.RecipientId
/**
* Represents a single row in the thread table.
*/
data class ThreadRecord(
val threadId: Long,
val date: Long,
val meaningfulMessages: Boolean,
val recipientId: RecipientId,
val read: ThreadTable.ReadStatus,
val type: Long,
val error: Int,
val snippet: String?,
val snippetType: Long,
val snippetUri: Uri?,
val snippetContentType: String?,
val snippetExtras: ThreadTable.Extra?,
val unreadCount: Int,
val archived: Boolean,
val status: Long,
val hasDeliveryReceipt: Boolean,
val hasReadReceipt: Boolean,
val expiresIn: Long,
val lastSeen: Long,
val hasSent: Boolean,
val lastScrolled: Long,
val pinnedOrder: Long?,
val unreadSelfMentionCount: Int,
val active: Boolean,
val snippetMessageExtras: MessageExtras?,
val snippetMessageId: Long
)
@@ -34,9 +34,9 @@ import org.signal.network.util.Preconditions;
import java.util.Objects;
/**
* Represents an entry in the {@link org.thoughtcrime.securesms.database.ThreadTable}.
* Similar to {@link ThreadRecord}, but with a populated recipient.
*/
public final class ThreadRecord {
public final class ThreadWithRecipient {
private final long threadId;
private final String body;
@@ -60,7 +60,7 @@ public final class ThreadRecord {
private final int unreadSelfMentionsCount;
private final MessageExtras messageExtras;
private ThreadRecord(@NonNull Builder builder) {
private ThreadWithRecipient(@NonNull Builder builder) {
this.threadId = builder.threadId;
this.body = builder.body;
this.recipient = builder.recipient;
@@ -255,7 +255,7 @@ public final class ThreadRecord {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreadRecord that = (ThreadRecord) o;
ThreadWithRecipient that = (ThreadWithRecipient) o;
return threadId == that.threadId &&
type == that.type &&
date == that.date &&
@@ -434,13 +434,13 @@ public final class ThreadRecord {
return this;
}
public ThreadRecord build() {
public ThreadWithRecipient build() {
if (distributionType == ThreadTable.DistributionTypes.CONVERSATION) {
Preconditions.checkArgument(threadId > 0);
Preconditions.checkNotNull(body);
Preconditions.checkNotNull(recipient);
}
return new ThreadRecord(this);
return new ThreadWithRecipient(this);
}
}
}
@@ -391,6 +391,7 @@ class JobController {
}
}
jobStorage.markJobAsRunning(job.getId(), System.currentTimeMillis());
runningJobs.put(job.getId(), new ActiveJobInfo(job, runnerName, timeoutMs == 0));
jobTracker.onStateChange(job, JobTracker.JobState.RUNNING);
@@ -6,7 +6,7 @@ import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -74,7 +74,7 @@ public class ConversationShortcutUpdateJob extends BaseJob {
List<Recipient> ranked = new ArrayList<>(maxShortcuts);
try (ThreadTable.Reader reader = threadTable.readerFor(threadTable.getRecentConversationList(maxShortcuts, false, false, false, true, true, false))) {
ThreadRecord record;
ThreadWithRecipient record;
while ((record = reader.getNext()) != null) {
ranked.add(record.getRecipient().resolve());
}
@@ -533,13 +533,12 @@ object DataMessageProcessor {
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
val targetThreadRecipientId = SignalDatabase.threads.getRecipientIdForThreadId(targetMessage.threadId)
if (targetThreadRecipientId == null) {
warn(envelope.clientTimestamp!!, "[handleReaction] Could not find a thread for the message! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
return null
}
val targetThreadRecipientId = targetThread.recipient.id
val groupRecord = SignalDatabase.groups.getGroup(targetThreadRecipientId).orNull()
if (groupRecord != null && !groupRecord.members.contains(senderRecipientId)) {
warn(envelope.clientTimestamp!!, "[handleReaction] Reaction author is not in the group! timestamp: " + targetSentTimestamp + " author: " + targetAuthor.id)
@@ -1275,13 +1274,13 @@ object DataMessageProcessor {
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
val targetThreadRecipientId = SignalDatabase.threads.getRecipientIdForThreadId(targetMessage.threadId)
if (targetThreadRecipientId == null) {
warn(envelope.clientTimestamp!!, "[handlePinMessage] Could not find a thread for the message! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
if (targetThread.recipient.id != threadRecipient.id) {
if (targetThreadRecipientId != threadRecipient.id) {
warn(envelope.clientTimestamp!!, "[handlePinMessage] Target message is in a different thread than the thread recipient! timestamp: ${pinMessage.targetSentTimestamp}")
return null
}
@@ -1367,13 +1366,13 @@ object DataMessageProcessor {
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
val targetThreadRecipientId = SignalDatabase.threads.getRecipientIdForThreadId(targetMessage.threadId)
if (targetThreadRecipientId == null) {
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Could not find a thread for the message! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
if (targetThread.recipient.id != threadRecipient.id) {
if (targetThreadRecipientId != threadRecipient.id) {
warn(envelope.clientTimestamp!!, "[handleUnpinMessage] Target message is in a different thread than the thread recipient! timestamp: ${unpinMessage.targetSentTimestamp}")
return null
}
@@ -1427,13 +1426,12 @@ object DataMessageProcessor {
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
val targetThreadRecipientId = SignalDatabase.threads.getRecipientIdForThreadId(targetMessage.threadId)
if (targetThreadRecipientId == null) {
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Could not find a thread for the message! timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
return null
}
val targetThreadRecipientId = targetThread.recipient.id
if (targetThreadRecipientId != threadRecipient.id) {
warn(envelope.clientTimestamp!!, "[handleAdminRemoteDelete] Target message is in a different thread than the admin delete! timestamp: $targetSentTimestamp")
return null
@@ -1616,17 +1614,17 @@ object DataMessageProcessor {
return null
}
val targetThread = SignalDatabase.threads.getThreadRecord(targetMessage.threadId)
if (targetThread == null) {
val targetThreadRecipientId = SignalDatabase.threads.getRecipientIdForThreadId(targetMessage.threadId)
if (targetThreadRecipientId == null) {
warn(envelope.clientTimestamp!!, "[handlePollValidation] Could not find a thread for the message. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
return null
}
val groupRecord = SignalDatabase.groups.getGroup(targetThread.recipient.id).orNull()
val groupRecord = SignalDatabase.groups.getGroup(targetThreadRecipientId).orNull()
if (groupRecord != null && !groupRecord.members.contains(senderRecipient.id)) {
warn(envelope.clientTimestamp!!, "[handlePollValidation] Sender is not in the group. timestamp: $targetSentTimestamp author: ${targetAuthor.id}")
return null
} else if (groupRecord == null && senderRecipient.id != targetThread.recipient.id && senderRecipient.id != Recipient.self().id) {
} else if (groupRecord == null && senderRecipient.id != targetThreadRecipientId && senderRecipient.id != Recipient.self().id) {
warn(envelope.clientTimestamp!!, "[handlePollValidation] Sender is not a part of the 1:1 thread!")
return null
}
@@ -9,7 +9,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import org.jetbrains.annotations.NotNull;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
@@ -17,8 +16,7 @@ import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.RecipientTable.MissingRecipientException;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.RecipientRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.LRUCache;
@@ -252,8 +250,8 @@ public final class LiveRecipientCache {
List<Recipient> recipients = new ArrayList<>();
try (ThreadTable.Reader reader = threadTable.readerFor(threadTable.getRecentConversationList(THREAD_CACHE_WARM_MAX, false, false))) {
int i = 0;
ThreadRecord record = null;
int i = 0;
ThreadWithRecipient record = null;
while ((record = reader.getNext()) != null && i < THREAD_CACHE_WARM_MAX) {
recipients.add(record.getRecipient());
@@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -82,8 +82,8 @@ public class SearchRepository {
@WorkerThread
public @NonNull ThreadSearchResult queryThreadsSync(@NonNull String query, boolean unreadOnly) {
long start = System.currentTimeMillis();
List<ThreadRecord> result = queryConversations(query, unreadOnly);
long start = System.currentTimeMillis();
List<ThreadWithRecipient> result = queryConversations(query, unreadOnly);
Log.d(TAG, "[threads] Search took " + (System.currentTimeMillis() - start) + " ms");
@@ -121,7 +121,7 @@ public class SearchRepository {
});
}
private @NonNull List<ThreadRecord> queryConversations(@NonNull String query, boolean unreadOnly) {
private @NonNull List<ThreadWithRecipient> queryConversations(@NonNull String query, boolean unreadOnly) {
if (Util.isEmpty(query)) {
return Collections.emptyList();
}
@@ -148,7 +148,7 @@ public class SearchRepository {
}
}
LinkedHashSet<ThreadRecord> output = new LinkedHashSet<>();
LinkedHashSet<ThreadWithRecipient> output = new LinkedHashSet<>();
output.addAll(getMatchingThreads(contactIds, unreadOnly));
output.addAll(getMatchingThreads(groupsByTitleIds, unreadOnly));
@@ -156,7 +156,7 @@ public class SearchRepository {
return new ArrayList<>(output);
}
private List<ThreadRecord> getMatchingThreads(@NonNull Collection<RecipientId> recipientIds, boolean unreadOnly) {
private List<ThreadWithRecipient> getMatchingThreads(@NonNull Collection<RecipientId> recipientIds, boolean unreadOnly) {
try (Cursor cursor = threadTable.getFilteredConversationList(new ArrayList<>(recipientIds), unreadOnly)) {
return readToList(cursor, new ThreadModelBuilder(threadTable));
}
@@ -455,7 +455,7 @@ public class SearchRepository {
return combined;
}
private static class ThreadModelBuilder implements ModelBuilder<ThreadRecord> {
private static class ThreadModelBuilder implements ModelBuilder<ThreadWithRecipient> {
private final ThreadTable threadTable;
@@ -464,7 +464,7 @@ public class SearchRepository {
}
@Override
public ThreadRecord build(@NonNull Cursor cursor) {
public ThreadWithRecipient build(@NonNull Cursor cursor) {
return threadTable.readerFor(cursor).getCurrent();
}
}
@@ -1,5 +1,5 @@
package org.thoughtcrime.securesms.search
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
data class ThreadSearchResult(val results: List<ThreadRecord>, val query: String)
data class ThreadSearchResult(val results: List<ThreadWithRecipient>, val query: String)