diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index 285e8b8a9..3368d6d9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -493,7 +493,8 @@ public final class ConversationListItem extends ConstraintLayout implements Bind } else if (!thread.isOutgoing() || thread.isOutgoingAudioCall() || thread.isOutgoingVideoCall() || - thread.isVerificationStatusChange()) + thread.isVerificationStatusChange() || + thread.isScheduledMessage()) { deliveryStatusIndicator.setNone(); alertView.setNone(); @@ -588,6 +589,8 @@ public final class ConversationListItem extends ConstraintLayout implements Bind return emphasisAdded(context, context.getString(R.string.ThreadRecord_secure_session_reset), defaultTint); } else if (MessageTypes.isLegacyType(thread.getType())) { return emphasisAdded(context, context.getString(R.string.MessageRecord_message_encrypted_with_a_legacy_protocol_version_that_is_no_longer_supported), defaultTint); + } else if (thread.isScheduledMessage()) { + return emphasisAdded(context, context.getString(R.string.ThreadRecord_scheduled_message), R.drawable.symbol_calendar_compact_light_16, defaultTint); } else if (MessageTypes.isDraftMessageType(thread.getType())) { String draftText = context.getString(R.string.ThreadRecord_draft); return emphasisAdded(context, draftText + " " + thread.getBody(), defaultTint); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java index dc8f24042..8f367a4fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java @@ -1716,8 +1716,8 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie } private @NonNull SqlUtil.Query buildMeaningfulMessagesQuery(long threadId) { - String query = THREAD_ID + " = ? AND " + STORY_TYPE + " = ? AND " + PARENT_STORY_ID + " <= ? AND " + SCHEDULED_DATE + " = ? AND (NOT " + TYPE + " & ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " & " + MessageTypes.GROUP_V2_LEAVE_BITS + " != " + MessageTypes.GROUP_V2_LEAVE_BITS + ")"; - return SqlUtil.buildQuery(query, threadId, 0, 0, -1, MessageTypes.IGNORABLE_TYPESMASK_WHEN_COUNTING, MessageTypes.PROFILE_CHANGE_TYPE, MessageTypes.CHANGE_NUMBER_TYPE, MessageTypes.SMS_EXPORT_TYPE, MessageTypes.BOOST_REQUEST_TYPE); + String query = THREAD_ID + " = ? AND " + STORY_TYPE + " = ? AND " + PARENT_STORY_ID + " <= ? AND " + "(NOT " + TYPE + " & ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " & " + MessageTypes.GROUP_V2_LEAVE_BITS + " != " + MessageTypes.GROUP_V2_LEAVE_BITS + ")"; + return SqlUtil.buildQuery(query, threadId, 0, 0, MessageTypes.IGNORABLE_TYPESMASK_WHEN_COUNTING, MessageTypes.PROFILE_CHANGE_TYPE, MessageTypes.CHANGE_NUMBER_TYPE, MessageTypes.SMS_EXPORT_TYPE, MessageTypes.BOOST_REQUEST_TYPE); } public void addFailures(long messageId, List failure) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java index 2df9d0654..a661937d2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java @@ -62,6 +62,8 @@ public final class ThreadBodyUtil { return format(EmojiStrings.CARD, getPaymentActivatedSummary(context, record)); } else if (record.isCallLog() && !record.isGroupCall()) { return new ThreadBody(getCallLogSummary(context, record)); + } else if (MessageRecordUtil.isScheduled(record)) { + return new ThreadBody(context.getString(R.string.ThreadRecord_scheduled_message)); } boolean hasImage = false; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index 38bf1efd9..484efd938 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.storage.StorageSyncHelper import org.thoughtcrime.securesms.util.ConversationUtil import org.thoughtcrime.securesms.util.JsonUtils import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.thoughtcrime.securesms.util.isScheduled import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.storage.SignalAccountRecord import org.whispersystems.signalservice.api.storage.SignalAccountRecord.PinnedConversation @@ -1344,31 +1345,36 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa val record: MessageRecord = try { messages.getConversationSnippet(threadId) } catch (e: NoSuchMessageException) { - Log.w(TAG, "Failed to get a conversation snippet for thread $threadId") + val scheduledMessage: MessageRecord? = messages.getScheduledMessagesInThread(threadId).lastOrNull() - if (shouldDelete) { - deleteConversation(threadId) + if (scheduledMessage == null) { + Log.w(TAG, "Failed to get a conversation snippet for thread $threadId") + if (shouldDelete) { + deleteConversation(threadId) + } + + if (isPinned) { + updateThread( + threadId = threadId, + meaningfulMessages = meaningfulMessages, + body = null, + attachment = null, + contentType = null, + extra = null, + date = 0, + status = 0, + deliveryReceiptCount = 0, + type = 0, + unarchive = unarchive, + expiresIn = 0, + readReceiptCount = 0 + ) + } + return true + } else { + Log.i(TAG, "Using scheduled message for conversation snippet") + scheduledMessage } - - if (isPinned) { - updateThread( - threadId = threadId, - meaningfulMessages = meaningfulMessages, - body = null, - attachment = null, - contentType = null, - extra = null, - date = 0, - status = 0, - deliveryReceiptCount = 0, - type = 0, - unarchive = unarchive, - expiresIn = 0, - readReceiptCount = 0 - ) - } - - return true } val drafts: DraftTable.Drafts = SignalDatabase.drafts.getDrafts(threadId) @@ -1575,7 +1581,9 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa } } - val extras: Extra? = if (record.isRemoteDelete) { + val extras: Extra? = if (record.isScheduled()) { + Extra.forScheduledMessage(individualRecipientId) + } else if (record.isRemoteDelete) { Extra.forRemoteDelete(individualRecipientId) } else if (record.isViewOnce) { Extra.forViewOnce(individualRecipientId) @@ -1778,7 +1786,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa @field:JsonProperty @param:JsonProperty("isGv2Invite") val isGv2Invite: Boolean = false, @field:JsonProperty @param:JsonProperty("groupAddedBy") val groupAddedBy: String? = null, @field:JsonProperty @param:JsonProperty("individualRecipientId") private val individualRecipientId: String, - @field:JsonProperty @param:JsonProperty("bodyRanges") val bodyRanges: String? = null + @field:JsonProperty @param:JsonProperty("bodyRanges") val bodyRanges: String? = null, + @field:JsonProperty @param:JsonProperty("isScheduled") val isScheduled: Boolean = false ) { fun getIndividualRecipientId(): String { @@ -1821,6 +1830,10 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa fun forBodyRanges(bodyRanges: BodyRangeList, individualRecipient: RecipientId): Extra { return Extra(individualRecipientId = individualRecipient.serialize(), bodyRanges = bodyRanges.serialize()) } + + fun forScheduledMessage(individualRecipient: RecipientId): Extra { + return Extra(individualRecipientId = individualRecipient.serialize(), isScheduled = true) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index e58c52582..099d4e35e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -185,6 +185,10 @@ public final class ThreadRecord { return StatusUtil.isDelivered(deliveryStatus, deliveryReceiptCount); } + public boolean isScheduledMessage() { + return extra != null && extra.isScheduled(); + } + public @Nullable RecipientId getGroupAddedBy() { if (extra != null && extra.getGroupAddedBy() != null) return RecipientId.from(extra.getGroupAddedBy()); else return null; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 542c5ea44..4290c25de 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1960,6 +1960,8 @@ Reacted %1$s to their story Payment + + Scheduled message Signal update