2015-01-15 21:35:35 +00:00
package org.thoughtcrime.securesms.database ;
import android.content.ContentValues ;
import android.content.Context ;
import android.database.Cursor ;
import android.text.TextUtils ;
2019-06-11 06:18:45 +00:00
import androidx.annotation.NonNull ;
2020-08-20 20:50:14 +00:00
import androidx.annotation.Nullable ;
2019-06-11 06:18:45 +00:00
2020-08-24 20:40:47 +00:00
import com.google.android.mms.pdu_alt.NotificationInd ;
2019-12-03 21:57:21 +00:00
2021-10-04 17:52:32 +00:00
import net.zetetic.database.sqlcipher.SQLiteStatement ;
2018-01-25 03:17:44 +00:00
2022-11-01 15:50:41 +00:00
import org.signal.core.util.CursorExtensionsKt ;
2022-09-19 17:49:19 +00:00
import org.signal.core.util.CursorUtil ;
2022-10-13 15:33:13 +00:00
import org.signal.core.util.SQLiteDatabaseExtensionsKt ;
2022-09-19 17:49:19 +00:00
import org.signal.core.util.SqlUtil ;
2020-12-04 23:31:58 +00:00
import org.signal.core.util.logging.Log ;
2022-03-24 17:23:23 +00:00
import org.signal.libsignal.protocol.IdentityKey ;
import org.signal.libsignal.protocol.util.Pair ;
2015-01-15 21:35:35 +00:00
import org.thoughtcrime.securesms.database.documents.Document ;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch ;
2021-10-01 18:21:41 +00:00
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet ;
2020-08-24 20:40:47 +00:00
import org.thoughtcrime.securesms.database.documents.NetworkFailure ;
2022-10-18 20:06:37 +00:00
import org.thoughtcrime.securesms.database.model.MessageExportStatus ;
2021-06-30 21:26:40 +00:00
import org.thoughtcrime.securesms.database.model.MessageId ;
2020-07-30 20:02:17 +00:00
import org.thoughtcrime.securesms.database.model.MessageRecord ;
2022-05-10 15:25:13 +00:00
import org.thoughtcrime.securesms.database.model.ParentStoryId ;
2020-07-30 20:02:17 +00:00
import org.thoughtcrime.securesms.database.model.SmsMessageRecord ;
2022-04-01 18:03:31 +00:00
import org.thoughtcrime.securesms.database.model.StoryResult ;
2022-10-13 15:46:13 +00:00
import org.thoughtcrime.securesms.database.model.StoryType ;
2022-02-25 19:06:12 +00:00
import org.thoughtcrime.securesms.database.model.StoryViewState ;
2022-08-30 18:22:40 +00:00
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState ;
2022-09-21 13:02:10 +00:00
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent ;
2020-11-24 15:54:41 +00:00
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange ;
2019-11-15 20:33:54 +00:00
import org.thoughtcrime.securesms.insights.InsightsConstants ;
2020-08-24 20:40:47 +00:00
import org.thoughtcrime.securesms.mms.IncomingMediaMessage ;
import org.thoughtcrime.securesms.mms.MmsException ;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage ;
2020-08-20 20:50:14 +00:00
import org.thoughtcrime.securesms.recipients.Recipient ;
2019-08-07 18:22:51 +00:00
import org.thoughtcrime.securesms.recipients.RecipientId ;
2020-08-24 20:40:47 +00:00
import org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo ;
2020-08-20 20:50:14 +00:00
import org.thoughtcrime.securesms.sms.IncomingTextMessage ;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage ;
2015-01-15 21:35:35 +00:00
import org.thoughtcrime.securesms.util.JsonUtils ;
2021-02-05 21:15:05 +00:00
import org.thoughtcrime.securesms.util.Util ;
2015-01-15 21:35:35 +00:00
2020-08-24 20:40:47 +00:00
import java.io.Closeable ;
2015-01-15 21:35:35 +00:00
import java.io.IOException ;
import java.util.ArrayList ;
2020-06-07 18:46:03 +00:00
import java.util.Collection ;
2015-01-15 21:35:35 +00:00
import java.util.Iterator ;
2022-08-02 14:46:45 +00:00
import java.util.LinkedList ;
2015-01-15 21:35:35 +00:00
import java.util.List ;
2020-02-19 22:08:34 +00:00
import java.util.Locale ;
2021-04-14 19:42:34 +00:00
import java.util.Map ;
2021-03-26 00:10:53 +00:00
import java.util.Objects ;
2022-03-14 19:49:46 +00:00
import java.util.Optional ;
2020-08-20 20:50:14 +00:00
import java.util.Set ;
2020-11-20 20:42:46 +00:00
import java.util.UUID ;
2015-01-15 21:35:35 +00:00
2022-09-27 12:26:44 +00:00
public abstract class MessageDatabase extends Database implements MmsSmsColumns , RecipientIdDatabaseReference , ThreadIdDatabaseReference {
2015-01-15 21:35:35 +00:00
2021-03-29 22:37:22 +00:00
private static final String TAG = Log . tag ( MessageDatabase . class ) ;
2015-01-15 21:35:35 +00:00
2021-08-02 21:46:56 +00:00
protected static final String THREAD_ID_WHERE = THREAD_ID + " = ?" ;
protected static final String [ ] THREAD_ID_PROJECTION = new String [ ] { THREAD_ID } ;
2021-07-29 18:07:39 +00:00
2021-11-18 17:36:52 +00:00
public MessageDatabase ( Context context , SignalDatabase databaseHelper ) {
2015-01-15 21:35:35 +00:00
super ( context , databaseHelper ) ;
}
protected abstract String getTableName ( ) ;
2019-11-12 14:18:57 +00:00
protected abstract String getTypeField ( ) ;
protected abstract String getDateSentColumnName ( ) ;
2020-08-04 17:13:59 +00:00
protected abstract String getDateReceivedColumnName ( ) ;
2015-01-15 21:35:35 +00:00
2020-08-20 20:50:14 +00:00
public abstract @Nullable RecipientId getOldestGroupUpdateSender ( long threadId , long minimumDateReceived ) ;
2020-08-24 20:40:47 +00:00
public abstract long getLatestGroupQuitTimestamp ( long threadId , long quitTimeBarrier ) ;
public abstract boolean isGroupQuitMessage ( long messageId ) ;
public abstract @Nullable Pair < RecipientId , Long > getOldestUnreadMentionDetails ( long threadId ) ;
public abstract int getUnreadMentionCount ( long threadId ) ;
2020-08-20 20:50:14 +00:00
public abstract long getThreadIdForMessage ( long id ) ;
public abstract int getMessageCountForThread ( long threadId ) ;
public abstract int getMessageCountForThread ( long threadId , long beforeTime ) ;
2021-05-18 19:19:33 +00:00
public abstract boolean hasMeaningfulMessage ( long threadId ) ;
2022-07-27 13:35:13 +00:00
public abstract int getIncomingMeaningfulMessageCountSince ( long threadId , long afterTime ) ;
2020-08-24 20:40:47 +00:00
public abstract Optional < MmsNotificationInfo > getNotification ( long messageId ) ;
2020-08-20 20:50:14 +00:00
public abstract Cursor getExpirationStartedMessages ( ) ;
public abstract SmsMessageRecord getSmsMessage ( long messageId ) throws NoSuchMessageException ;
2020-08-24 20:40:47 +00:00
public abstract Reader getMessages ( Collection < Long > messageIds ) ;
2020-08-20 20:50:14 +00:00
public abstract Cursor getMessageCursor ( long messageId ) ;
2020-08-24 20:40:47 +00:00
public abstract OutgoingMediaMessage getOutgoingMessage ( long messageId ) throws MmsException , NoSuchMessageException ;
2020-08-20 20:50:14 +00:00
public abstract MessageRecord getMessageRecord ( long messageId ) throws NoSuchMessageException ;
2021-06-24 15:22:20 +00:00
public abstract @Nullable MessageRecord getMessageRecordOrNull ( long messageId ) ;
2020-08-20 20:50:14 +00:00
public abstract boolean hasReceivedAnyCallsSince ( long threadId , long timestamp ) ;
2020-08-24 20:40:47 +00:00
public abstract @Nullable ViewOnceExpirationInfo getNearestExpiringViewOnceMessage ( ) ;
public abstract boolean isSent ( long messageId ) ;
2020-11-04 20:00:12 +00:00
public abstract List < MessageRecord > getProfileChangeDetailsRecords ( long threadId , long afterTimestamp ) ;
2021-05-05 16:49:18 +00:00
public abstract Set < Long > getAllRateLimitedMessageIds ( ) ;
2022-09-28 14:47:37 +00:00
public abstract Cursor getUnexportedInsecureMessages ( int limit ) ;
2022-10-20 02:11:31 +00:00
public abstract long getUnexportedInsecureMessagesEstimatedSize ( ) ;
2022-08-30 18:22:40 +00:00
public abstract void deleteExportedMessages ( ) ;
2020-08-20 20:50:14 +00:00
2017-10-02 21:54:55 +00:00
public abstract void markExpireStarted ( long messageId ) ;
public abstract void markExpireStarted ( long messageId , long startTime ) ;
2020-06-07 18:46:03 +00:00
public abstract void markExpireStarted ( Collection < Long > messageId , long startTime ) ;
2017-10-02 21:54:55 +00:00
2020-08-20 20:50:14 +00:00
public abstract void markAsEndSession ( long id ) ;
public abstract void markAsInvalidVersionKeyExchange ( long id ) ;
public abstract void markAsSecure ( long id ) ;
public abstract void markAsInsecure ( long id ) ;
public abstract void markAsPush ( long id ) ;
public abstract void markAsForcedSms ( long id ) ;
2021-05-05 16:49:18 +00:00
public abstract void markAsRateLimited ( long id ) ;
public abstract void clearRateLimitStatus ( Collection < Long > ids ) ;
2020-08-20 20:50:14 +00:00
public abstract void markAsDecryptFailed ( long id ) ;
public abstract void markAsNoSession ( long id ) ;
public abstract void markAsUnsupportedProtocolVersion ( long id ) ;
public abstract void markAsInvalidMessage ( long id ) ;
public abstract void markAsLegacyVersion ( long id ) ;
public abstract void markAsOutbox ( long id ) ;
public abstract void markAsPendingInsecureSmsFallback ( long id ) ;
2017-10-02 21:54:55 +00:00
public abstract void markAsSent ( long messageId , boolean secure ) ;
2018-10-11 23:45:22 +00:00
public abstract void markUnidentified ( long messageId , boolean unidentified ) ;
2021-08-24 15:11:48 +00:00
public abstract void markAsSentFailed ( long id ) ;
2020-04-15 18:56:58 +00:00
public abstract void markAsSending ( long messageId ) ;
public abstract void markAsRemoteDelete ( long messageId ) ;
2020-11-04 14:42:45 +00:00
public abstract void markAsMissedCall ( long id , boolean isVideoOffer ) ;
2020-08-20 20:50:14 +00:00
public abstract void markAsNotified ( long id ) ;
public abstract void markSmsStatus ( long id , int status ) ;
2020-08-24 20:40:47 +00:00
public abstract void markDownloadState ( long messageId , long state ) ;
public abstract void markIncomingNotificationReceived ( long threadId ) ;
2022-05-02 17:29:42 +00:00
public abstract void markGiftRedemptionCompleted ( long messageId ) ;
public abstract void markGiftRedemptionStarted ( long messageId ) ;
public abstract void markGiftRedemptionFailed ( long messageId ) ;
2020-08-20 20:50:14 +00:00
2022-10-13 15:46:13 +00:00
public abstract Set < MessageUpdate > incrementReceiptCount ( SyncMessageId messageId , long timestamp , @NonNull ReceiptType receiptType , @NonNull MessageQualifier messageType ) ;
2022-08-02 14:46:45 +00:00
2020-08-20 20:50:14 +00:00
public abstract List < MarkedMessageInfo > setEntireThreadRead ( long threadId ) ;
public abstract List < MarkedMessageInfo > setMessagesReadSince ( long threadId , long timestamp ) ;
public abstract List < MarkedMessageInfo > setAllMessagesRead ( ) ;
2021-06-30 18:36:00 +00:00
public abstract InsertResult updateBundleMessageBody ( long messageId , String body ) ;
2020-11-20 13:16:37 +00:00
public abstract @NonNull List < MarkedMessageInfo > getViewedIncomingMessages ( long threadId ) ;
public abstract @Nullable MarkedMessageInfo setIncomingMessageViewed ( long messageId ) ;
2021-04-28 19:21:34 +00:00
public abstract @NonNull List < MarkedMessageInfo > setIncomingMessagesViewed ( @NonNull List < Long > messageIds ) ;
2022-05-16 13:24:48 +00:00
public abstract @NonNull List < MarkedMessageInfo > setOutgoingGiftsRevealed ( @NonNull List < Long > messageIds ) ;
2020-08-20 20:50:14 +00:00
2020-08-24 20:40:47 +00:00
public abstract void addFailures ( long messageId , List < NetworkFailure > failure ) ;
2021-10-01 18:21:41 +00:00
public abstract void setNetworkFailures ( long messageId , Set < NetworkFailure > failures ) ;
2020-08-20 20:50:14 +00:00
2020-11-05 17:41:22 +00:00
public abstract @NonNull Pair < Long , Long > insertReceivedCall ( @NonNull RecipientId address , boolean isVideoOffer ) ;
public abstract @NonNull Pair < Long , Long > insertOutgoingCall ( @NonNull RecipientId address , boolean isVideoOffer ) ;
2020-11-04 14:42:45 +00:00
public abstract @NonNull Pair < Long , Long > insertMissedCall ( @NonNull RecipientId address , long timestamp , boolean isVideoOffer ) ;
2020-12-02 18:20:38 +00:00
public abstract void insertOrUpdateGroupCall ( @NonNull RecipientId groupRecipientId ,
@NonNull RecipientId sender ,
long timestamp ,
@Nullable String peekGroupCallEraId ,
@NonNull Collection < UUID > peekJoinedUuids ,
boolean isCallFull ) ;
2020-12-07 22:27:35 +00:00
public abstract void insertOrUpdateGroupCall ( @NonNull RecipientId groupRecipientId ,
@NonNull RecipientId sender ,
long timestamp ,
@Nullable String messageGroupCallEraId ) ;
2020-12-02 18:20:38 +00:00
public abstract boolean updatePreviousGroupCall ( long threadId , @Nullable String peekGroupCallEraId , @NonNull Collection < UUID > peekJoinedUuids , boolean isCallFull ) ;
2020-08-20 20:50:14 +00:00
public abstract Optional < InsertResult > insertMessageInbox ( IncomingTextMessage message , long type ) ;
public abstract Optional < InsertResult > insertMessageInbox ( IncomingTextMessage message ) ;
2020-08-24 20:40:47 +00:00
public abstract Optional < InsertResult > insertMessageInbox ( IncomingMediaMessage retrieved , String contentLocation , long threadId ) throws MmsException ;
public abstract Pair < Long , Long > insertMessageInbox ( @NonNull NotificationInd notification , int subscriptionId ) ;
public abstract Optional < InsertResult > insertSecureDecryptedMessageInbox ( IncomingMediaMessage retrieved , long threadId ) throws MmsException ;
2021-05-14 18:03:35 +00:00
public abstract @NonNull InsertResult insertChatSessionRefreshedMessage ( @NonNull RecipientId recipientId , long senderDeviceId , long sentTimestamp ) ;
public abstract void insertBadDecryptMessage ( @NonNull RecipientId recipientId , int senderDevice , long sentTimestamp , long receivedTimestamp , long threadId ) ;
2020-08-20 20:50:14 +00:00
public abstract long insertMessageOutbox ( long threadId , OutgoingTextMessage message , boolean forceSms , long date , InsertListener insertListener ) ;
2020-08-24 20:40:47 +00:00
public abstract long insertMessageOutbox ( @NonNull OutgoingMediaMessage message , long threadId , boolean forceSms , @Nullable SmsDatabase . InsertListener insertListener ) throws MmsException ;
public abstract long insertMessageOutbox ( @NonNull OutgoingMediaMessage message , long threadId , boolean forceSms , int defaultReceiptStatus , @Nullable SmsDatabase . InsertListener insertListener ) throws MmsException ;
2020-08-20 20:50:14 +00:00
public abstract void insertProfileNameChangeMessages ( @NonNull Recipient recipient , @NonNull String newProfileName , @NonNull String previousProfileName ) ;
2020-11-24 15:54:41 +00:00
public abstract void insertGroupV1MigrationEvents ( @NonNull RecipientId recipientId , long threadId , @NonNull GroupMigrationMembershipChange membershipChange ) ;
2022-11-08 00:11:54 +00:00
public abstract void insertNumberChangeMessages ( @NonNull RecipientId recipientId ) ;
2022-01-31 17:46:44 +00:00
public abstract void insertBoostRequestMessage ( @NonNull RecipientId recipientId , long threadId ) ;
2022-09-21 13:02:10 +00:00
public abstract void insertThreadMergeEvent ( @NonNull RecipientId recipientId , long threadId , @NonNull ThreadMergeEvent event ) ;
2022-10-13 15:33:13 +00:00
public abstract void insertSmsExportMessage ( @NonNull RecipientId recipientId , long threadId ) ;
2020-08-20 20:50:14 +00:00
public abstract boolean deleteMessage ( long messageId ) ;
abstract void deleteThread ( long threadId ) ;
2021-08-13 16:50:45 +00:00
abstract int deleteMessagesInThreadBeforeDate ( long threadId , long date ) ;
2020-08-20 20:50:14 +00:00
abstract void deleteThreads ( @NonNull Set < Long > threadIds ) ;
abstract void deleteAllThreads ( ) ;
2020-09-03 21:52:44 +00:00
abstract void deleteAbandonedMessages ( ) ;
2022-11-10 15:47:14 +00:00
public abstract void deleteRemotelyDeletedStory ( long messageId ) ;
2020-08-20 20:50:14 +00:00
2020-10-15 16:55:08 +00:00
public abstract List < MessageRecord > getMessagesInThreadAfterInclusive ( long threadId , long timestamp , long limit ) ;
2020-08-24 20:40:47 +00:00
public abstract SQLiteDatabase beginTransaction ( ) ;
public abstract void endTransaction ( SQLiteDatabase database ) ;
public abstract void setTransactionSuccessful ( ) ;
public abstract void endTransaction ( ) ;
public abstract SQLiteStatement createInsertStatement ( SQLiteDatabase database ) ;
2020-08-20 20:50:14 +00:00
public abstract void ensureMigration ( ) ;
2020-04-15 18:56:58 +00:00
2022-02-24 17:40:28 +00:00
public abstract boolean isStory ( long messageId ) ;
public abstract @NonNull Reader getOutgoingStoriesTo ( @NonNull RecipientId recipientId ) ;
2022-07-05 15:38:35 +00:00
public abstract @NonNull Reader getAllOutgoingStories ( boolean reverse , int limit ) ;
2022-05-10 14:01:51 +00:00
public abstract @NonNull Reader getAllOutgoingStoriesAt ( long sentTimestamp ) ;
2022-10-19 17:53:31 +00:00
public abstract @NonNull List < MarkedMessageInfo > markAllIncomingStoriesRead ( ) ;
2022-07-25 18:33:34 +00:00
public abstract @NonNull List < StoryResult > getOrderedStoryRecipientsAndIds ( boolean isOutgoingOnly ) ;
2022-10-19 17:53:31 +00:00
public abstract void markOnboardingStoryRead ( ) ;
2022-07-05 15:38:35 +00:00
public abstract @NonNull Reader getAllStoriesFor ( @NonNull RecipientId recipientId , int limit ) ;
2022-02-24 17:40:28 +00:00
public abstract @NonNull MessageId getStoryId ( @NonNull RecipientId authorId , long sentTimestamp ) throws NoSuchMessageException ;
public abstract int getNumberOfStoryReplies ( long parentStoryId ) ;
2022-04-06 17:37:25 +00:00
public abstract @NonNull List < RecipientId > getUnreadStoryThreadRecipientIds ( ) ;
2022-03-16 21:11:16 +00:00
public abstract boolean containsStories ( long threadId ) ;
2022-02-24 17:40:28 +00:00
public abstract boolean hasSelfReplyInStory ( long parentStoryId ) ;
2022-09-21 19:50:07 +00:00
public abstract boolean hasGroupReplyOrReactionInStory ( long parentStoryId ) ;
2022-02-24 17:40:28 +00:00
public abstract @NonNull Cursor getStoryReplies ( long parentStoryId ) ;
2022-07-13 17:52:32 +00:00
public abstract @Nullable Long getOldestStorySendTimestamp ( boolean hasSeenReleaseChannelStories ) ;
2022-07-01 17:29:50 +00:00
public abstract int deleteStoriesOlderThan ( long timestamp , boolean hasSeenReleaseChannelStories ) ;
2022-04-21 20:29:02 +00:00
public abstract @NonNull MessageDatabase . Reader getUnreadStories ( @NonNull RecipientId recipientId , int limit ) ;
2022-05-10 15:25:13 +00:00
public abstract @Nullable ParentStoryId . GroupReply getParentStoryIdForGroupReply ( long messageId ) ;
2022-05-11 13:03:09 +00:00
public abstract void deleteGroupStoryReplies ( long parentStoryId ) ;
2022-05-12 14:45:19 +00:00
public abstract boolean isOutgoingStoryAlreadyInDatabase ( @NonNull RecipientId recipientId , long sentTimestamp ) ;
2022-05-12 18:37:28 +00:00
public abstract @NonNull List < MarkedMessageInfo > setGroupStoryMessagesReadSince ( long threadId , long groupStoryId , long sinceTimestamp ) ;
2022-10-13 15:46:13 +00:00
public abstract @NonNull List < StoryType > getStoryTypes ( @NonNull List < MessageId > messageIds ) ;
2022-04-21 20:29:02 +00:00
2022-02-25 19:06:12 +00:00
public abstract @NonNull StoryViewState getStoryViewState ( @NonNull RecipientId recipientId ) ;
2022-04-21 20:29:02 +00:00
public abstract void updateViewedStories ( @NonNull Set < SyncMessageId > syncMessageIds ) ;
2022-02-24 17:40:28 +00:00
2021-02-05 21:15:05 +00:00
final @NonNull String getOutgoingTypeClause ( ) {
List < String > segments = new ArrayList < > ( Types . OUTGOING_MESSAGE_TYPES . length ) ;
for ( long outgoingMessageType : Types . OUTGOING_MESSAGE_TYPES ) {
segments . add ( "(" + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + " = " + outgoingMessageType + ")" ) ;
}
return Util . join ( segments , " OR " ) ;
}
2020-07-30 20:02:17 +00:00
2019-11-12 14:18:57 +00:00
final int getInsecureMessagesSentForThread ( long threadId ) {
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalReadableDatabase ( ) ;
2019-11-12 14:18:57 +00:00
String [ ] projection = new String [ ] { "COUNT(*)" } ;
String query = THREAD_ID + " = ? AND " + getOutgoingInsecureMessageClause ( ) + " AND " + getDateSentColumnName ( ) + " > ?" ;
2019-11-15 20:33:54 +00:00
String [ ] args = new String [ ] { String . valueOf ( threadId ) , String . valueOf ( System . currentTimeMillis ( ) - InsightsConstants . PERIOD_IN_MILLIS ) } ;
2019-11-12 14:18:57 +00:00
try ( Cursor cursor = db . query ( getTableName ( ) , projection , query , args , null , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
return cursor . getInt ( 0 ) ;
} else {
return 0 ;
}
}
}
2019-11-15 20:33:54 +00:00
final int getInsecureMessageCountForInsights ( ) {
return getMessageCountForRecipientsAndType ( getOutgoingInsecureMessageClause ( ) ) ;
2019-11-12 14:18:57 +00:00
}
2022-10-13 15:33:13 +00:00
public int getInsecureMessageCount ( ) {
try ( Cursor cursor = getReadableDatabase ( ) . query ( getTableName ( ) , SqlUtil . COUNT , getInsecureMessageClause ( ) , null , null , null , null ) ) {
if ( cursor . moveToFirst ( ) ) {
return cursor . getInt ( 0 ) ;
}
}
return 0 ;
}
public boolean hasSmsExportMessage ( long threadId ) {
return SQLiteDatabaseExtensionsKt . exists ( getReadableDatabase ( ) , getTableName ( ) , THREAD_ID_WHERE + " AND " + getTypeField ( ) + " = ?" , threadId , Types . SMS_EXPORT_TYPE ) ;
}
2019-11-15 20:33:54 +00:00
final int getSecureMessageCountForInsights ( ) {
return getMessageCountForRecipientsAndType ( getOutgoingSecureMessageClause ( ) ) ;
2019-11-12 14:18:57 +00:00
}
2020-02-19 22:08:34 +00:00
final int getSecureMessageCount ( long threadId ) {
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalReadableDatabase ( ) ;
2020-02-19 22:08:34 +00:00
String [ ] projection = new String [ ] { "COUNT(*)" } ;
String query = getSecureMessageClause ( ) + "AND " + MmsSmsColumns . THREAD_ID + " = ?" ;
String [ ] args = new String [ ] { String . valueOf ( threadId ) } ;
try ( Cursor cursor = db . query ( getTableName ( ) , projection , query , args , null , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
return cursor . getInt ( 0 ) ;
} else {
return 0 ;
}
}
}
final int getOutgoingSecureMessageCount ( long threadId ) {
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalReadableDatabase ( ) ;
2020-02-19 22:08:34 +00:00
String [ ] projection = new String [ ] { "COUNT(*)" } ;
2021-09-16 16:36:00 +00:00
String query = getOutgoingSecureMessageClause ( ) +
"AND " + MmsSmsColumns . THREAD_ID + " = ? " +
"AND (" + getTypeField ( ) + " & " + Types . GROUP_LEAVE_BIT + " = 0 OR " + getTypeField ( ) + " & " + Types . GROUP_V2_BIT + " = " + Types . GROUP_V2_BIT + ")" ;
2020-02-19 22:08:34 +00:00
String [ ] args = new String [ ] { String . valueOf ( threadId ) } ;
try ( Cursor cursor = db . query ( getTableName ( ) , projection , query , args , null , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
return cursor . getInt ( 0 ) ;
} else {
return 0 ;
}
}
}
2022-08-02 14:46:45 +00:00
/ * *
* Handles a synchronized read message .
* @param messageId An id representing the author - timestamp pair of the message that was read on a linked device . Note that the author could be self when
* syncing read receipts for reactions .
* /
final @NonNull MmsSmsDatabase . TimestampReadResult setTimestampReadFromSyncMessage ( SyncMessageId messageId , long proposedExpireStarted , @NonNull Map < Long , Long > threadToLatestRead ) {
SQLiteDatabase database = databaseHelper . getSignalWritableDatabase ( ) ;
List < Pair < Long , Long > > expiring = new LinkedList < > ( ) ;
String [ ] projection = new String [ ] { ID , THREAD_ID , EXPIRES_IN , EXPIRE_STARTED } ;
String query = getDateSentColumnName ( ) + " = ? AND (" + RECIPIENT_ID + " = ? OR (" + RECIPIENT_ID + " = ? AND " + getOutgoingTypeClause ( ) + "))" ;
String [ ] args = SqlUtil . buildArgs ( messageId . getTimetamp ( ) , messageId . getRecipientId ( ) , Recipient . self ( ) . getId ( ) ) ;
List < Long > threads = new LinkedList < > ( ) ;
try ( Cursor cursor = database . query ( getTableName ( ) , projection , query , args , null , null , null ) ) {
while ( cursor . moveToNext ( ) ) {
long id = cursor . getLong ( cursor . getColumnIndexOrThrow ( ID ) ) ;
long threadId = cursor . getLong ( cursor . getColumnIndexOrThrow ( THREAD_ID ) ) ;
long expiresIn = cursor . getLong ( cursor . getColumnIndexOrThrow ( EXPIRES_IN ) ) ;
long expireStarted = cursor . getLong ( cursor . getColumnIndexOrThrow ( EXPIRE_STARTED ) ) ;
expireStarted = expireStarted > 0 ? Math . min ( proposedExpireStarted , expireStarted ) : proposedExpireStarted ;
ContentValues values = new ContentValues ( ) ;
values . put ( READ , 1 ) ;
values . put ( REACTIONS_UNREAD , 0 ) ;
values . put ( REACTIONS_LAST_SEEN , System . currentTimeMillis ( ) ) ;
if ( expiresIn > 0 ) {
values . put ( EXPIRE_STARTED , expireStarted ) ;
expiring . add ( new Pair < > ( id , expiresIn ) ) ;
}
database . update ( getTableName ( ) , values , ID_WHERE , SqlUtil . buildArgs ( id ) ) ;
threads . add ( threadId ) ;
Long latest = threadToLatestRead . get ( threadId ) ;
threadToLatestRead . put ( threadId , ( latest ! = null ) ? Math . max ( latest , messageId . getTimetamp ( ) ) : messageId . getTimetamp ( ) ) ;
}
}
return new MmsSmsDatabase . TimestampReadResult ( expiring , threads ) ;
}
2019-11-15 20:33:54 +00:00
private int getMessageCountForRecipientsAndType ( String typeClause ) {
2019-11-12 14:18:57 +00:00
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalReadableDatabase ( ) ;
2019-11-12 14:18:57 +00:00
String [ ] projection = new String [ ] { "COUNT(*)" } ;
2019-11-15 20:33:54 +00:00
String query = typeClause + " AND " + getDateSentColumnName ( ) + " > ?" ;
String [ ] args = new String [ ] { String . valueOf ( System . currentTimeMillis ( ) - InsightsConstants . PERIOD_IN_MILLIS ) } ;
2019-11-12 14:18:57 +00:00
try ( Cursor cursor = db . query ( getTableName ( ) , projection , query , args , null , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
return cursor . getInt ( 0 ) ;
} else {
return 0 ;
}
}
}
private String getOutgoingInsecureMessageClause ( ) {
return "(" + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_SENT_TYPE + " AND NOT (" + getTypeField ( ) + " & " + Types . SECURE_MESSAGE_BIT + ")" ;
}
private String getOutgoingSecureMessageClause ( ) {
return "(" + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_SENT_TYPE + " AND (" + getTypeField ( ) + " & " + ( Types . SECURE_MESSAGE_BIT | Types . PUSH_MESSAGE_BIT ) + ")" ;
}
2020-02-19 22:08:34 +00:00
private String getSecureMessageClause ( ) {
String isSent = "(" + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_SENT_TYPE ;
String isReceived = "(" + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_INBOX_TYPE ;
String isSecure = "(" + getTypeField ( ) + " & " + ( Types . SECURE_MESSAGE_BIT | Types . PUSH_MESSAGE_BIT ) + ")" ;
return String . format ( Locale . ENGLISH , "(%s OR %s) AND %s" , isSent , isReceived , isSecure ) ;
}
2022-08-30 18:22:40 +00:00
protected String getInsecureMessageClause ( ) {
2022-10-13 15:33:13 +00:00
return getInsecureMessageClause ( - 1 ) ;
}
protected String getInsecureMessageClause ( long threadId ) {
2022-10-20 02:11:31 +00:00
String isSent = "(" + getTableName ( ) + "." + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_SENT_TYPE ;
String isReceived = "(" + getTableName ( ) + "." + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_INBOX_TYPE ;
String isSecure = "(" + getTableName ( ) + "." + getTypeField ( ) + " & " + ( Types . SECURE_MESSAGE_BIT | Types . PUSH_MESSAGE_BIT ) + ")" ;
String isNotSecure = "(" + getTableName ( ) + "." + getTypeField ( ) + " <= " + ( Types . BASE_TYPE_MASK | Types . MESSAGE_ATTRIBUTE_MASK ) + ")" ;
2022-09-28 14:47:37 +00:00
2022-10-13 15:33:13 +00:00
String whereClause = String . format ( Locale . ENGLISH , "(%s OR %s) AND NOT %s AND %s" , isSent , isReceived , isSecure , isNotSecure ) ;
if ( threadId ! = - 1 ) {
2022-10-20 02:11:31 +00:00
whereClause + = " AND " + getTableName ( ) + "." + THREAD_ID + " = " + threadId ;
2022-10-13 15:33:13 +00:00
}
return whereClause ;
2022-09-28 14:47:37 +00:00
}
public int getUnexportedInsecureMessagesCount ( ) {
2022-10-13 15:33:13 +00:00
return getUnexportedInsecureMessagesCount ( - 1 ) ;
}
public int getUnexportedInsecureMessagesCount ( long threadId ) {
2022-10-18 20:06:37 +00:00
try ( Cursor cursor = getWritableDatabase ( ) . query ( getTableName ( ) , SqlUtil . COUNT , getInsecureMessageClause ( threadId ) + " AND " + EXPORTED + " < ?" , SqlUtil . buildArgs ( MessageExportStatus . EXPORTED ) , null , null , null ) ) {
2022-09-28 14:47:37 +00:00
if ( cursor . moveToFirst ( ) ) {
return cursor . getInt ( 0 ) ;
}
}
2022-08-30 18:22:40 +00:00
2022-09-28 14:47:37 +00:00
return 0 ;
2022-08-30 18:22:40 +00:00
}
2022-11-16 16:42:41 +00:00
/ * *
* Resets the exported state and exported flag so messages can be re - exported .
* /
public void clearExportState ( ) {
ContentValues values = new ContentValues ( 2 ) ;
values . putNull ( EXPORT_STATE ) ;
values . put ( EXPORTED , MessageExportStatus . UNEXPORTED . serialize ( ) ) ;
SQLiteDatabaseExtensionsKt . update ( getWritableDatabase ( ) , getTableName ( ) )
. values ( values )
. where ( EXPORT_STATE + " IS NOT NULL OR " + EXPORTED + " != ?" , MessageExportStatus . UNEXPORTED )
. run ( ) ;
}
2022-10-18 20:06:37 +00:00
/ * *
* Reset the exported status ( not state ) to the default for clearing errors .
* /
public void clearInsecureMessageExportedErrorStatus ( ) {
ContentValues values = new ContentValues ( 1 ) ;
values . put ( EXPORTED , MessageExportStatus . UNEXPORTED . getCode ( ) ) ;
SQLiteDatabaseExtensionsKt . update ( getWritableDatabase ( ) , getTableName ( ) )
. values ( values )
2022-10-20 02:11:31 +00:00
. where ( EXPORTED + " < ?" , MessageExportStatus . UNEXPORTED )
2022-10-18 20:06:37 +00:00
. run ( ) ;
}
2020-08-04 17:13:59 +00:00
public void setReactionsSeen ( long threadId , long sinceTimestamp ) {
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalWritableDatabase ( ) ;
2019-12-03 21:57:21 +00:00
ContentValues values = new ContentValues ( ) ;
2019-12-06 00:10:37 +00:00
String whereClause = THREAD_ID + " = ? AND " + REACTIONS_UNREAD + " = ?" ;
String [ ] whereArgs = new String [ ] { String . valueOf ( threadId ) , "1" } ;
2019-12-03 21:57:21 +00:00
2020-08-04 17:13:59 +00:00
if ( sinceTimestamp > - 1 ) {
whereClause + = " AND " + getDateReceivedColumnName ( ) + " <= " + sinceTimestamp ;
}
2019-12-03 21:57:21 +00:00
values . put ( REACTIONS_UNREAD , 0 ) ;
values . put ( REACTIONS_LAST_SEEN , System . currentTimeMillis ( ) ) ;
db . update ( getTableName ( ) , values , whereClause , whereArgs ) ;
}
public void setAllReactionsSeen ( ) {
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalWritableDatabase ( ) ;
2020-06-24 14:34:52 +00:00
ContentValues values = new ContentValues ( ) ;
String query = REACTIONS_UNREAD + " != ?" ;
String [ ] args = new String [ ] { "0" } ;
2019-12-03 21:57:21 +00:00
values . put ( REACTIONS_UNREAD , 0 ) ;
values . put ( REACTIONS_LAST_SEEN , System . currentTimeMillis ( ) ) ;
2020-06-24 14:34:52 +00:00
db . update ( getTableName ( ) , values , query , args ) ;
2019-12-03 21:57:21 +00:00
}
2020-11-11 19:49:48 +00:00
public void setNotifiedTimestamp ( long timestamp , @NonNull List < Long > ids ) {
if ( ids . isEmpty ( ) ) {
return ;
}
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalWritableDatabase ( ) ;
2022-04-26 17:59:24 +00:00
SqlUtil . Query where = SqlUtil . buildSingleCollectionQuery ( ID , ids ) ;
2020-11-11 19:49:48 +00:00
ContentValues values = new ContentValues ( ) ;
values . put ( NOTIFIED_TIMESTAMP , timestamp ) ;
db . update ( getTableName ( ) , values , where . getWhere ( ) , where . getWhereArgs ( ) ) ;
}
2019-08-07 18:22:51 +00:00
public void addMismatchedIdentity ( long messageId , @NonNull RecipientId recipientId , IdentityKey identityKey ) {
2015-01-15 21:35:35 +00:00
try {
addToDocument ( messageId , MISMATCHED_IDENTITIES ,
2019-08-07 18:22:51 +00:00
new IdentityKeyMismatch ( recipientId , identityKey ) ,
2021-10-01 18:21:41 +00:00
IdentityKeyMismatchSet . class ) ;
2015-01-15 21:35:35 +00:00
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
}
}
2019-08-07 18:22:51 +00:00
public void removeMismatchedIdentity ( long messageId , @NonNull RecipientId recipientId , IdentityKey identityKey ) {
2015-01-15 21:35:35 +00:00
try {
removeFromDocument ( messageId , MISMATCHED_IDENTITIES ,
2019-08-07 18:22:51 +00:00
new IdentityKeyMismatch ( recipientId , identityKey ) ,
2021-10-01 18:21:41 +00:00
IdentityKeyMismatchSet . class ) ;
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
}
}
public void setMismatchedIdentities ( long messageId , @NonNull Set < IdentityKeyMismatch > mismatches ) {
try {
setDocument ( databaseHelper . getSignalWritableDatabase ( ) , messageId , MISMATCHED_IDENTITIES , new IdentityKeyMismatchSet ( mismatches ) ) ;
2015-01-15 21:35:35 +00:00
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
}
}
2021-05-17 13:43:37 +00:00
public @NonNull List < ReportSpamData > getReportSpamMessageServerGuids ( long threadId , long timestamp ) {
2021-08-18 14:16:54 +00:00
SQLiteDatabase db = databaseHelper . getSignalReadableDatabase ( ) ;
2021-05-17 13:43:37 +00:00
String query = THREAD_ID + " = ? AND " + getDateReceivedColumnName ( ) + " <= ?" ;
String [ ] args = SqlUtil . buildArgs ( threadId , timestamp ) ;
List < ReportSpamData > data = new ArrayList < > ( ) ;
try ( Cursor cursor = db . query ( getTableName ( ) , new String [ ] { RECIPIENT_ID , SERVER_GUID , getDateReceivedColumnName ( ) } , query , args , null , null , getDateReceivedColumnName ( ) + " DESC" , "3" ) ) {
while ( cursor . moveToNext ( ) ) {
RecipientId id = RecipientId . from ( CursorUtil . requireLong ( cursor , RECIPIENT_ID ) ) ;
String serverGuid = CursorUtil . requireString ( cursor , SERVER_GUID ) ;
long dateReceived = CursorUtil . requireLong ( cursor , getDateReceivedColumnName ( ) ) ;
if ( ! Util . isEmpty ( serverGuid ) ) {
data . add ( new ReportSpamData ( id , serverGuid , dateReceived ) ) ;
}
}
}
return data ;
}
2022-11-01 15:50:41 +00:00
public List < Long > getIncomingPaymentRequestThreads ( ) {
Cursor cursor = SQLiteDatabaseExtensionsKt . select ( getReadableDatabase ( ) , "DISTINCT " + THREAD_ID )
. from ( getTableName ( ) )
2022-11-10 19:45:45 +00:00
. where ( "(" + getTypeField ( ) + " & " + Types . BASE_TYPE_MASK + ") = " + Types . BASE_INBOX_TYPE + " AND (" + getTypeField ( ) + " & ?) != 0" , Types . SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST )
2022-11-01 15:50:41 +00:00
. run ( ) ;
return CursorExtensionsKt . readToList ( cursor , c - > CursorUtil . requireLong ( c , THREAD_ID ) ) ;
}
2022-09-27 12:26:44 +00:00
@Override
public void remapRecipient ( @NonNull RecipientId fromId , @NonNull RecipientId toId ) {
ContentValues values = new ContentValues ( ) ;
values . put ( RECIPIENT_ID , toId . serialize ( ) ) ;
getWritableDatabase ( ) . update ( getTableName ( ) , values , RECIPIENT_ID + " = ?" , SqlUtil . buildArgs ( toId ) ) ;
}
@Override
public void remapThread ( long fromId , long toId ) {
ContentValues values = new ContentValues ( ) ;
values . put ( SmsDatabase . THREAD_ID , toId ) ;
getWritableDatabase ( ) . update ( getTableName ( ) , values , THREAD_ID + " = ?" , SqlUtil . buildArgs ( fromId ) ) ;
}
2021-11-11 18:12:51 +00:00
void updateReactionsUnread ( SQLiteDatabase db , long messageId , boolean hasReactions , boolean isRemoval ) {
try {
boolean isOutgoing = getMessageRecord ( messageId ) . isOutgoing ( ) ;
ContentValues values = new ContentValues ( ) ;
2019-12-03 21:57:21 +00:00
2021-11-11 18:12:51 +00:00
if ( ! hasReactions ) {
values . put ( REACTIONS_UNREAD , 0 ) ;
} else if ( ! isRemoval ) {
values . put ( REACTIONS_UNREAD , 1 ) ;
2019-12-03 21:57:21 +00:00
}
2021-11-11 18:12:51 +00:00
if ( isOutgoing & & hasReactions ) {
values . put ( NOTIFIED , 0 ) ;
}
if ( values . size ( ) > 0 ) {
db . update ( getTableName ( ) , values , ID_WHERE , SqlUtil . buildArgs ( messageId ) ) ;
}
} catch ( NoSuchMessageException e ) {
Log . w ( TAG , "Failed to find message " + messageId ) ;
2019-12-03 21:57:21 +00:00
}
}
2015-01-15 21:35:35 +00:00
protected < D extends Document < I > , I > void removeFromDocument ( long messageId , String column , I object , Class < D > clazz ) throws IOException {
2021-08-18 14:16:54 +00:00
SQLiteDatabase database = databaseHelper . getSignalWritableDatabase ( ) ;
2015-01-15 21:35:35 +00:00
database . beginTransaction ( ) ;
try {
D document = getDocument ( database , messageId , column , clazz ) ;
2021-10-01 18:21:41 +00:00
Iterator < I > iterator = document . getItems ( ) . iterator ( ) ;
2015-01-15 21:35:35 +00:00
while ( iterator . hasNext ( ) ) {
I item = iterator . next ( ) ;
if ( item . equals ( object ) ) {
iterator . remove ( ) ;
break ;
}
}
setDocument ( database , messageId , column , document ) ;
database . setTransactionSuccessful ( ) ;
} finally {
database . endTransaction ( ) ;
}
}
protected < T extends Document < I > , I > void addToDocument ( long messageId , String column , final I object , Class < T > clazz ) throws IOException {
List < I > list = new ArrayList < I > ( ) { {
add ( object ) ;
} } ;
addToDocument ( messageId , column , list , clazz ) ;
}
protected < T extends Document < I > , I > void addToDocument ( long messageId , String column , List < I > objects , Class < T > clazz ) throws IOException {
2021-08-18 14:16:54 +00:00
SQLiteDatabase database = databaseHelper . getSignalWritableDatabase ( ) ;
2015-01-15 21:35:35 +00:00
database . beginTransaction ( ) ;
try {
T document = getDocument ( database , messageId , column , clazz ) ;
2021-10-01 18:21:41 +00:00
document . getItems ( ) . addAll ( objects ) ;
2015-01-15 21:35:35 +00:00
setDocument ( database , messageId , column , document ) ;
database . setTransactionSuccessful ( ) ;
} finally {
database . endTransaction ( ) ;
}
}
2021-10-01 18:21:41 +00:00
protected void setDocument ( SQLiteDatabase database , long messageId , String column , Document document ) throws IOException {
2015-01-15 21:35:35 +00:00
ContentValues contentValues = new ContentValues ( ) ;
if ( document = = null | | document . size ( ) = = 0 ) {
contentValues . put ( column , ( String ) null ) ;
} else {
contentValues . put ( column , JsonUtils . toJson ( document ) ) ;
}
database . update ( getTableName ( ) , contentValues , ID_WHERE , new String [ ] { String . valueOf ( messageId ) } ) ;
}
private < D extends Document > D getDocument ( SQLiteDatabase database , long messageId ,
String column , Class < D > clazz )
{
Cursor cursor = null ;
try {
cursor = database . query ( getTableName ( ) , new String [ ] { column } ,
ID_WHERE , new String [ ] { String . valueOf ( messageId ) } ,
null , null , null ) ;
if ( cursor ! = null & & cursor . moveToNext ( ) ) {
String document = cursor . getString ( cursor . getColumnIndexOrThrow ( column ) ) ;
try {
if ( ! TextUtils . isEmpty ( document ) ) {
return JsonUtils . fromJson ( document , clazz ) ;
}
} catch ( IOException e ) {
Log . w ( TAG , e ) ;
}
}
try {
return clazz . newInstance ( ) ;
2015-05-26 20:12:11 +00:00
} catch ( InstantiationException e ) {
throw new AssertionError ( e ) ;
} catch ( IllegalAccessException e ) {
2015-01-15 21:35:35 +00:00
throw new AssertionError ( e ) ;
}
} finally {
if ( cursor ! = null )
cursor . close ( ) ;
}
}
2016-02-20 01:07:41 +00:00
2019-12-03 21:57:21 +00:00
private long getThreadId ( @NonNull SQLiteDatabase db , long messageId ) {
String [ ] projection = new String [ ] { THREAD_ID } ;
String query = ID + " = ?" ;
String [ ] args = new String [ ] { String . valueOf ( messageId ) } ;
try ( Cursor cursor = db . query ( getTableName ( ) , projection , query , args , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
return cursor . getLong ( cursor . getColumnIndexOrThrow ( THREAD_ID ) ) ;
}
}
return - 1 ;
}
2020-11-20 13:16:37 +00:00
protected enum ReceiptType {
READ ( READ_RECEIPT_COUNT , GroupReceiptDatabase . STATUS_READ ) ,
DELIVERY ( DELIVERY_RECEIPT_COUNT , GroupReceiptDatabase . STATUS_DELIVERED ) ,
VIEWED ( VIEWED_RECEIPT_COUNT , GroupReceiptDatabase . STATUS_VIEWED ) ;
private final String columnName ;
private final int groupStatus ;
ReceiptType ( String columnName , int groupStatus ) {
this . columnName = columnName ;
this . groupStatus = groupStatus ;
}
public String getColumnName ( ) {
return columnName ;
}
public int getGroupStatus ( ) {
return groupStatus ;
}
}
2016-02-20 01:07:41 +00:00
public static class SyncMessageId {
2019-08-07 18:22:51 +00:00
private final RecipientId recipientId ;
private final long timetamp ;
2016-02-20 01:07:41 +00:00
2019-08-07 18:22:51 +00:00
public SyncMessageId ( @NonNull RecipientId recipientId , long timetamp ) {
this . recipientId = recipientId ;
this . timetamp = timetamp ;
2016-02-20 01:07:41 +00:00
}
2019-08-07 18:22:51 +00:00
public RecipientId getRecipientId ( ) {
return recipientId ;
2016-02-20 01:07:41 +00:00
}
public long getTimetamp ( ) {
return timetamp ;
}
2022-04-29 19:09:54 +00:00
@Override
public boolean equals ( Object o ) {
if ( this = = o ) return true ;
if ( o = = null | | getClass ( ) ! = o . getClass ( ) ) return false ;
final SyncMessageId that = ( SyncMessageId ) o ;
return timetamp = = that . timetamp & & Objects . equals ( recipientId , that . recipientId ) ;
}
@Override
public int hashCode ( ) {
return Objects . hash ( recipientId , timetamp ) ;
}
2016-02-20 01:07:41 +00:00
}
2016-10-10 18:13:37 +00:00
public static class ExpirationInfo {
private final long id ;
private final long expiresIn ;
private final long expireStarted ;
private final boolean mms ;
public ExpirationInfo ( long id , long expiresIn , long expireStarted , boolean mms ) {
this . id = id ;
this . expiresIn = expiresIn ;
this . expireStarted = expireStarted ;
this . mms = mms ;
}
public long getId ( ) {
return id ;
}
public long getExpiresIn ( ) {
return expiresIn ;
}
public long getExpireStarted ( ) {
return expireStarted ;
}
public boolean isMms ( ) {
return mms ;
}
}
public static class MarkedMessageInfo {
2020-02-19 22:08:34 +00:00
private final long threadId ;
2016-10-10 18:13:37 +00:00
private final SyncMessageId syncMessageId ;
2021-06-30 21:26:40 +00:00
private final MessageId messageId ;
2016-10-10 18:13:37 +00:00
private final ExpirationInfo expirationInfo ;
2021-06-30 21:26:40 +00:00
public MarkedMessageInfo ( long threadId , @NonNull SyncMessageId syncMessageId , @NonNull MessageId messageId , @Nullable ExpirationInfo expirationInfo ) {
2020-02-19 22:08:34 +00:00
this . threadId = threadId ;
2016-10-10 18:13:37 +00:00
this . syncMessageId = syncMessageId ;
2021-06-30 21:26:40 +00:00
this . messageId = messageId ;
2016-10-10 18:13:37 +00:00
this . expirationInfo = expirationInfo ;
}
2020-02-19 22:08:34 +00:00
public long getThreadId ( ) {
return threadId ;
}
2021-06-30 21:26:40 +00:00
public @NonNull SyncMessageId getSyncMessageId ( ) {
2016-10-10 18:13:37 +00:00
return syncMessageId ;
}
2021-06-30 21:26:40 +00:00
public @NonNull MessageId getMessageId ( ) {
return messageId ;
}
public @Nullable ExpirationInfo getExpirationInfo ( ) {
2016-10-10 18:13:37 +00:00
return expirationInfo ;
}
}
2017-01-22 21:52:36 +00:00
public static class InsertResult {
private final long messageId ;
private final long threadId ;
public InsertResult ( long messageId , long threadId ) {
this . messageId = messageId ;
this . threadId = threadId ;
}
public long getMessageId ( ) {
return messageId ;
}
public long getThreadId ( ) {
return threadId ;
}
}
2020-08-20 20:50:14 +00:00
2020-08-24 20:40:47 +00:00
public static class MmsNotificationInfo {
private final RecipientId from ;
private final String contentLocation ;
private final String transactionId ;
private final int subscriptionId ;
MmsNotificationInfo ( @NonNull RecipientId from , String contentLocation , String transactionId , int subscriptionId ) {
this . from = from ;
this . contentLocation = contentLocation ;
this . transactionId = transactionId ;
this . subscriptionId = subscriptionId ;
}
public String getContentLocation ( ) {
return contentLocation ;
}
public String getTransactionId ( ) {
return transactionId ;
}
public int getSubscriptionId ( ) {
return subscriptionId ;
}
public @NonNull RecipientId getFrom ( ) {
return from ;
}
}
2022-01-03 23:46:10 +00:00
static class MessageUpdate {
private final long threadId ;
private final MessageId messageId ;
2021-03-26 00:10:53 +00:00
2022-01-03 23:46:10 +00:00
MessageUpdate ( long threadId , @NonNull MessageId messageId ) {
this . threadId = threadId ;
this . messageId = messageId ;
2021-03-26 00:10:53 +00:00
}
public long getThreadId ( ) {
return threadId ;
}
2022-01-03 23:46:10 +00:00
public @NonNull MessageId getMessageId ( ) {
return messageId ;
2021-03-26 00:10:53 +00:00
}
@Override
public boolean equals ( Object o ) {
if ( this = = o ) return true ;
if ( o = = null | | getClass ( ) ! = o . getClass ( ) ) return false ;
2022-01-03 23:46:10 +00:00
final MessageUpdate that = ( MessageUpdate ) o ;
return threadId = = that . threadId & & messageId . equals ( that . messageId ) ;
2021-03-26 00:10:53 +00:00
}
@Override
public int hashCode ( ) {
2022-01-03 23:46:10 +00:00
return Objects . hash ( threadId , messageId ) ;
2021-03-26 00:10:53 +00:00
}
}
2020-08-20 20:50:14 +00:00
public interface InsertListener {
void onComplete ( ) ;
}
2020-08-24 20:40:47 +00:00
2022-07-01 17:29:50 +00:00
/ * *
* Allows the developer to safely iterate over and close a cursor containing
* data for MessageRecord objects . Supports for - each loops as well as try - with - resources
* blocks .
*
* Readers are considered "one-shot" and it ' s on the caller to decide what needs
* to be done with the data . Once read , a reader cannot be read from again . This
* is by design , since reading data out of a cursor involves object creations and
* lookups , so it is in the best interest of app performance to only read out the
* data once . If you need to parse the list multiple times , it is recommended that
* you copy the iterable out into a normal List , or use extension methods such as
* partition .
*
* This reader does not support removal , since this would be considered a destructive
* database call .
* /
public interface Reader extends Closeable , Iterable < MessageRecord > {
/ * *
* @deprecated Use the Iterable interface instead .
* /
@Deprecated
2020-08-24 20:40:47 +00:00
MessageRecord getNext ( ) ;
2022-07-01 17:29:50 +00:00
/ * *
* @deprecated Use the Iterable interface instead .
* /
@Deprecated
2020-08-24 20:40:47 +00:00
MessageRecord getCurrent ( ) ;
2022-07-01 17:29:50 +00:00
2022-08-30 18:22:40 +00:00
/ * *
* Pulls the export state out of the query , if it is present .
* /
@NonNull MessageExportState getMessageExportStateForCurrentRecord ( ) ;
2022-07-01 17:29:50 +00:00
/ * *
* From the { @link Closeable } interface , removing the IOException requirement .
* /
2020-08-24 20:40:47 +00:00
void close ( ) ;
}
2021-05-17 13:43:37 +00:00
public static class ReportSpamData {
private final RecipientId recipientId ;
private final String serverGuid ;
private final long dateReceived ;
public ReportSpamData ( RecipientId recipientId , String serverGuid , long dateReceived ) {
this . recipientId = recipientId ;
this . serverGuid = serverGuid ;
this . dateReceived = dateReceived ;
}
public @NonNull RecipientId getRecipientId ( ) {
return recipientId ;
}
public @NonNull String getServerGuid ( ) {
return serverGuid ;
}
public long getDateReceived ( ) {
return dateReceived ;
}
}
2022-10-13 15:46:13 +00:00
/ * *
* Describes which messages to act on . This is used when incrementing receipts .
* Specifically , this was added to support stories having separate viewed receipt settings .
* /
public enum MessageQualifier {
/ * *
* A normal database message ( i . e . not a story )
* /
NORMAL ,
/ * *
* A story message
* /
STORY ,
/ * *
* Both normal and story message
* /
ALL
}
2015-01-15 21:35:35 +00:00
}