mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-03 20:46:09 +01:00
Improved send performance.
This commit is contained in:
committed by
Michelle Tang
parent
b2f450d849
commit
5d45914a08
@@ -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,
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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()
|
||||
|
||||
+2
-2
@@ -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
|
||||
) {
|
||||
|
||||
+3
-3
@@ -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.
|
||||
|
||||
+2
-2
@@ -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))
|
||||
|
||||
+2
-2
@@ -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());
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
+9
-9
@@ -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)
|
||||
|
||||
+2
-2
@@ -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,
|
||||
|
||||
+1
-1
@@ -221,7 +221,7 @@ object ConversationListSearchModels {
|
||||
|
||||
(itemView as ConversationListItem).bindThread(
|
||||
lifecycleOwner,
|
||||
model.thread.threadRecord,
|
||||
model.thread.threadWithRecipient,
|
||||
requestManager,
|
||||
Locale.getDefault(),
|
||||
emptySet(),
|
||||
|
||||
+5
-5
@@ -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
-5
@@ -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
|
||||
)
|
||||
+6
-6
@@ -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);
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user