Search in sources :

Example 1 with BodyRangeList

use of org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList in project Signal-Android by WhisperSystems.

the class MmsDatabase method insertMessageOutbox.

@Override
public long insertMessageOutbox(@NonNull OutgoingMediaMessage message, long threadId, boolean forceSms, int defaultReceiptStatus, @Nullable SmsDatabase.InsertListener insertListener) throws MmsException {
    SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
    long type = Types.BASE_SENDING_TYPE;
    if (message.isSecure())
        type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
    if (forceSms)
        type |= Types.MESSAGE_FORCE_SMS_BIT;
    if (message.isGroup()) {
        OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (OutgoingGroupUpdateMessage) message;
        if (outgoingGroupUpdateMessage.isV2Group()) {
            type |= Types.GROUP_V2_BIT | Types.GROUP_UPDATE_BIT;
            if (outgoingGroupUpdateMessage.isJustAGroupLeave()) {
                type |= Types.GROUP_LEAVE_BIT;
            }
        } else {
            MessageGroupContext.GroupV1Properties properties = outgoingGroupUpdateMessage.requireGroupV1Properties();
            if (properties.isUpdate())
                type |= Types.GROUP_UPDATE_BIT;
            else if (properties.isQuit())
                type |= Types.GROUP_LEAVE_BIT;
        }
    }
    if (message.isExpirationUpdate()) {
        type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
    }
    Map<RecipientId, EarlyReceiptCache.Receipt> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
    ContentValues contentValues = new ContentValues();
    contentValues.put(DATE_SENT, message.getSentTimeMillis());
    contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
    contentValues.put(MESSAGE_BOX, type);
    contentValues.put(THREAD_ID, threadId);
    contentValues.put(READ, 1);
    contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
    contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
    contentValues.put(EXPIRES_IN, message.getExpiresIn());
    contentValues.put(VIEW_ONCE, message.isViewOnce());
    contentValues.put(RECIPIENT_ID, message.getRecipient().getId().serialize());
    contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getCount).sum());
    contentValues.put(RECEIPT_TIMESTAMP, Stream.of(earlyDeliveryReceipts.values()).mapToLong(EarlyReceiptCache.Receipt::getTimestamp).max().orElse(-1));
    if (message.getRecipient().isSelf() && hasAudioAttachment(message.getAttachments())) {
        contentValues.put(VIEWED_RECEIPT_COUNT, 1L);
    }
    List<Attachment> quoteAttachments = new LinkedList<>();
    if (message.getOutgoingQuote() != null) {
        MentionUtil.UpdatedBodyAndMentions updated = MentionUtil.updateBodyAndMentionsWithPlaceholders(message.getOutgoingQuote().getText(), message.getOutgoingQuote().getMentions());
        contentValues.put(QUOTE_ID, message.getOutgoingQuote().getId());
        contentValues.put(QUOTE_AUTHOR, message.getOutgoingQuote().getAuthor().serialize());
        contentValues.put(QUOTE_BODY, updated.getBodyAsString());
        contentValues.put(QUOTE_MISSING, message.getOutgoingQuote().isOriginalMissing() ? 1 : 0);
        BodyRangeList mentionsList = MentionUtil.mentionsToBodyRangeList(updated.getMentions());
        if (mentionsList != null) {
            contentValues.put(QUOTE_MENTIONS, mentionsList.toByteArray());
        }
        quoteAttachments.addAll(message.getOutgoingQuote().getAttachments());
    }
    MentionUtil.UpdatedBodyAndMentions updatedBodyAndMentions = MentionUtil.updateBodyAndMentionsWithPlaceholders(message.getBody(), message.getMentions());
    long messageId = insertMediaMessage(threadId, updatedBodyAndMentions.getBodyAsString(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), updatedBodyAndMentions.getMentions(), null, contentValues, insertListener, false);
    if (message.getRecipient().isGroup()) {
        OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (message instanceof OutgoingGroupUpdateMessage) ? (OutgoingGroupUpdateMessage) message : null;
        GroupReceiptDatabase receiptDatabase = SignalDatabase.groupReceipts();
        Set<RecipientId> members = new HashSet<>();
        if (outgoingGroupUpdateMessage != null && outgoingGroupUpdateMessage.isV2Group()) {
            MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties();
            members.addAll(Stream.of(groupV2Properties.getAllActivePendingAndRemovedMembers()).distinct().map(uuid -> RecipientId.from(ACI.from(uuid), null)).toList());
            members.remove(Recipient.self().getId());
        } else {
            members.addAll(Stream.of(SignalDatabase.groups().getGroupMembers(message.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF)).map(Recipient::getId).toList());
        }
        receiptDatabase.insert(members, messageId, defaultReceiptStatus, message.getSentTimeMillis());
        for (RecipientId recipientId : earlyDeliveryReceipts.keySet()) {
            receiptDatabase.update(recipientId, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1);
        }
    }
    SignalDatabase.threads().updateLastSeenAndMarkSentAndLastScrolledSilenty(threadId);
    ApplicationDependencies.getDatabaseObserver().notifyMessageInsertObservers(threadId, new MessageId(messageId, true));
    notifyConversationListListeners();
    TrimThreadJob.enqueueAsync(threadId);
    return messageId;
}
Also used : ContentValues(android.content.ContentValues) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) BodyRangeList(org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList) Attachment(org.thoughtcrime.securesms.attachments.Attachment) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) MmsNotificationAttachment(org.thoughtcrime.securesms.attachments.MmsNotificationAttachment) Recipient(org.thoughtcrime.securesms.recipients.Recipient) OutgoingGroupUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage) LinkedList(java.util.LinkedList) MessageGroupContext(org.thoughtcrime.securesms.mms.MessageGroupContext) HashSet(java.util.HashSet) MessageId(org.thoughtcrime.securesms.database.model.MessageId)

Example 2 with BodyRangeList

use of org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList in project Signal-Android by WhisperSystems.

the class MmsDatabase method insertMediaMessage.

private long insertMediaMessage(long threadId, @Nullable String body, @NonNull List<Attachment> attachments, @NonNull List<Attachment> quoteAttachments, @NonNull List<Contact> sharedContacts, @NonNull List<LinkPreview> linkPreviews, @NonNull List<Mention> mentions, @Nullable BodyRangeList messageRanges, @NonNull ContentValues contentValues, @Nullable InsertListener insertListener, boolean updateThread) throws MmsException {
    SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
    AttachmentDatabase partsDatabase = SignalDatabase.attachments();
    MentionDatabase mentionDatabase = SignalDatabase.mentions();
    boolean mentionsSelf = Stream.of(mentions).filter(m -> Recipient.resolved(m.getRecipientId()).isSelf()).findFirst().isPresent();
    List<Attachment> allAttachments = new LinkedList<>();
    List<Attachment> contactAttachments = Stream.of(sharedContacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList();
    List<Attachment> previewAttachments = Stream.of(linkPreviews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).toList();
    allAttachments.addAll(attachments);
    allAttachments.addAll(contactAttachments);
    allAttachments.addAll(previewAttachments);
    contentValues.put(BODY, body);
    contentValues.put(PART_COUNT, allAttachments.size());
    contentValues.put(MENTIONS_SELF, mentionsSelf ? 1 : 0);
    if (messageRanges != null) {
        contentValues.put(MESSAGE_RANGES, messageRanges.toByteArray());
    }
    db.beginTransaction();
    try {
        long messageId = db.insert(TABLE_NAME, null, contentValues);
        mentionDatabase.insert(threadId, messageId, mentions);
        Map<Attachment, AttachmentId> insertedAttachments = partsDatabase.insertAttachmentsForMessage(messageId, allAttachments, quoteAttachments);
        String serializedContacts = getSerializedSharedContacts(insertedAttachments, sharedContacts);
        String serializedPreviews = getSerializedLinkPreviews(insertedAttachments, linkPreviews);
        if (!TextUtils.isEmpty(serializedContacts)) {
            ContentValues contactValues = new ContentValues();
            contactValues.put(SHARED_CONTACTS, serializedContacts);
            SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
            int rows = database.update(TABLE_NAME, contactValues, ID + " = ?", new String[] { String.valueOf(messageId) });
            if (rows <= 0) {
                Log.w(TAG, "Failed to update message with shared contact data.");
            }
        }
        if (!TextUtils.isEmpty(serializedPreviews)) {
            ContentValues contactValues = new ContentValues();
            contactValues.put(LINK_PREVIEWS, serializedPreviews);
            SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
            int rows = database.update(TABLE_NAME, contactValues, ID + " = ?", new String[] { String.valueOf(messageId) });
            if (rows <= 0) {
                Log.w(TAG, "Failed to update message with link preview data.");
            }
        }
        db.setTransactionSuccessful();
        return messageId;
    } finally {
        db.endTransaction();
        if (insertListener != null) {
            insertListener.onComplete();
        }
        long contentValuesThreadId = contentValues.getAsLong(THREAD_ID);
        if (updateThread) {
            SignalDatabase.threads().setLastScrolled(contentValuesThreadId, 0);
            SignalDatabase.threads().update(threadId, true);
        }
    }
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) SlideDeck(org.thoughtcrime.securesms.mms.SlideDeck) GroupMigrationMembershipChange(org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange) LinkPreview(org.thoughtcrime.securesms.linkpreview.LinkPreview) NonNull(androidx.annotation.NonNull) Avatar(org.thoughtcrime.securesms.contactshare.Contact.Avatar) MessageGroupContext(org.thoughtcrime.securesms.mms.MessageGroupContext) Mention(org.thoughtcrime.securesms.database.model.Mention) MessageRecord(org.thoughtcrime.securesms.database.model.MessageRecord) SecureRandom(java.security.SecureRandom) JSONException(org.json.JSONException) JsonUtils(org.thoughtcrime.securesms.util.JsonUtils) JSONObject(org.json.JSONObject) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) BodyRangeList(org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList) SqlUtil(org.thoughtcrime.securesms.util.SqlUtil) IdentityKeyMismatchSet(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet) Map(java.util.Map) ViewOnceUtil(org.thoughtcrime.securesms.revealable.ViewOnceUtil) Recipient(org.thoughtcrime.securesms.recipients.Recipient) OutgoingTextMessage(org.thoughtcrime.securesms.sms.OutgoingTextMessage) OutgoingGroupUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) NotificationInd(com.google.android.mms.pdu_alt.NotificationInd) ACI(org.whispersystems.signalservice.api.push.ACI) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) Collection(java.util.Collection) OutgoingSecureMediaMessage(org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage) Set(java.util.Set) UUID(java.util.UUID) OutgoingExpirationUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage) Log(org.signal.core.util.logging.Log) NetworkFailureSet(org.thoughtcrime.securesms.database.documents.NetworkFailureSet) List(java.util.List) Nullable(androidx.annotation.Nullable) ContentValues(android.content.ContentValues) IncomingTextMessage(org.thoughtcrime.securesms.sms.IncomingTextMessage) Attachment(org.thoughtcrime.securesms.attachments.Attachment) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) MediaUtil(org.thoughtcrime.securesms.util.MediaUtil) Context(android.content.Context) Stream(com.annimon.stream.Stream) Util(org.thoughtcrime.securesms.util.Util) HashMap(java.util.HashMap) SQLiteStatement(net.zetetic.database.sqlcipher.SQLiteStatement) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) ArrayList(java.util.ArrayList) TextSecurePreferences(org.thoughtcrime.securesms.util.TextSecurePreferences) HashSet(java.util.HashSet) Pair(org.whispersystems.libsignal.util.Pair) MmsNotificationAttachment(org.thoughtcrime.securesms.attachments.MmsNotificationAttachment) NotificationMmsMessageRecord(org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord) QuoteModel(org.thoughtcrime.securesms.mms.QuoteModel) MediaMmsMessageRecord(org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord) LinkedList(java.util.LinkedList) PduHeaders(com.google.android.mms.pdu_alt.PduHeaders) Cursor(android.database.Cursor) MessageId(org.thoughtcrime.securesms.database.model.MessageId) Collectors(com.annimon.stream.Collectors) Contact(org.thoughtcrime.securesms.contactshare.Contact) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) MmsException(org.thoughtcrime.securesms.mms.MmsException) TextUtils(android.text.TextUtils) IOException(java.io.IOException) Quote(org.thoughtcrime.securesms.database.model.Quote) Optional(org.whispersystems.libsignal.util.guava.Optional) CursorUtil(org.thoughtcrime.securesms.util.CursorUtil) ViewOnceExpirationInfo(org.thoughtcrime.securesms.revealable.ViewOnceExpirationInfo) AttachmentId(org.thoughtcrime.securesms.attachments.AttachmentId) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) TrimThreadJob(org.thoughtcrime.securesms.jobs.TrimThreadJob) SmsMessageRecord(org.thoughtcrime.securesms.database.model.SmsMessageRecord) Collections(java.util.Collections) JSONArray(org.json.JSONArray) IncomingMediaMessage(org.thoughtcrime.securesms.mms.IncomingMediaMessage) ContentValues(android.content.ContentValues) Attachment(org.thoughtcrime.securesms.attachments.Attachment) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) MmsNotificationAttachment(org.thoughtcrime.securesms.attachments.MmsNotificationAttachment) LinkedList(java.util.LinkedList) AttachmentId(org.thoughtcrime.securesms.attachments.AttachmentId) Contact(org.thoughtcrime.securesms.contactshare.Contact)

Example 3 with BodyRangeList

use of org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList in project Signal-Android by WhisperSystems.

the class MmsDatabase method insertMessageInbox.

private Optional<InsertResult> insertMessageInbox(IncomingMediaMessage retrieved, String contentLocation, long threadId, long mailbox) throws MmsException {
    if (threadId == -1 || retrieved.isGroupMessage()) {
        threadId = getThreadIdFor(retrieved);
    }
    ContentValues contentValues = new ContentValues();
    contentValues.put(DATE_SENT, retrieved.getSentTimeMillis());
    contentValues.put(DATE_SERVER, retrieved.getServerTimeMillis());
    contentValues.put(RECIPIENT_ID, retrieved.getFrom().serialize());
    contentValues.put(MESSAGE_BOX, mailbox);
    contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
    contentValues.put(THREAD_ID, threadId);
    contentValues.put(CONTENT_LOCATION, contentLocation);
    contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED);
    contentValues.put(DATE_RECEIVED, retrieved.isPushMessage() ? retrieved.getReceivedTimeMillis() : generatePduCompatTimestamp(retrieved.getReceivedTimeMillis()));
    contentValues.put(PART_COUNT, retrieved.getAttachments().size());
    contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId());
    contentValues.put(EXPIRES_IN, retrieved.getExpiresIn());
    contentValues.put(VIEW_ONCE, retrieved.isViewOnce() ? 1 : 0);
    contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0);
    contentValues.put(UNIDENTIFIED, retrieved.isUnidentified());
    contentValues.put(SERVER_GUID, retrieved.getServerGuid());
    if (!contentValues.containsKey(DATE_SENT)) {
        contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
    }
    List<Attachment> quoteAttachments = new LinkedList<>();
    if (retrieved.getQuote() != null) {
        contentValues.put(QUOTE_ID, retrieved.getQuote().getId());
        contentValues.put(QUOTE_BODY, retrieved.getQuote().getText().toString());
        contentValues.put(QUOTE_AUTHOR, retrieved.getQuote().getAuthor().serialize());
        contentValues.put(QUOTE_MISSING, retrieved.getQuote().isOriginalMissing() ? 1 : 0);
        BodyRangeList mentionsList = MentionUtil.mentionsToBodyRangeList(retrieved.getQuote().getMentions());
        if (mentionsList != null) {
            contentValues.put(QUOTE_MENTIONS, mentionsList.toByteArray());
        }
        quoteAttachments = retrieved.getQuote().getAttachments();
    }
    if (retrieved.isPushMessage() && isDuplicate(retrieved, threadId)) {
        Log.w(TAG, "Ignoring duplicate media message (" + retrieved.getSentTimeMillis() + ")");
        return Optional.absent();
    }
    long messageId = insertMediaMessage(threadId, retrieved.getBody(), retrieved.getAttachments(), quoteAttachments, retrieved.getSharedContacts(), retrieved.getLinkPreviews(), retrieved.getMentions(), retrieved.getMessageRanges(), contentValues, null, true);
    if (!Types.isExpirationTimerUpdate(mailbox)) {
        SignalDatabase.threads().incrementUnread(threadId, 1);
        SignalDatabase.threads().update(threadId, true);
    }
    notifyConversationListeners(threadId);
    return Optional.of(new InsertResult(messageId, threadId));
}
Also used : ContentValues(android.content.ContentValues) BodyRangeList(org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList) Attachment(org.thoughtcrime.securesms.attachments.Attachment) DatabaseAttachment(org.thoughtcrime.securesms.attachments.DatabaseAttachment) MmsNotificationAttachment(org.thoughtcrime.securesms.attachments.MmsNotificationAttachment) LinkedList(java.util.LinkedList)

Example 4 with BodyRangeList

use of org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList in project Signal-Android by WhisperSystems.

the class MentionUtil method mentionsToBodyRangeList.

@Nullable
public static BodyRangeList mentionsToBodyRangeList(@Nullable List<Mention> mentions) {
    if (mentions == null || mentions.isEmpty()) {
        return null;
    }
    BodyRangeList.Builder builder = BodyRangeList.newBuilder();
    for (Mention mention : mentions) {
        String uuid = Recipient.resolved(mention.getRecipientId()).requireServiceId().toString();
        builder.addRanges(BodyRangeList.BodyRange.newBuilder().setMentionUuid(uuid).setStart(mention.getStart()).setLength(mention.getLength()));
    }
    return builder.build();
}
Also used : BodyRangeList(org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList) Mention(org.thoughtcrime.securesms.database.model.Mention) Nullable(androidx.annotation.Nullable)

Aggregations

BodyRangeList (org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList)4 ContentValues (android.content.ContentValues)3 LinkedList (java.util.LinkedList)3 Attachment (org.thoughtcrime.securesms.attachments.Attachment)3 DatabaseAttachment (org.thoughtcrime.securesms.attachments.DatabaseAttachment)3 MmsNotificationAttachment (org.thoughtcrime.securesms.attachments.MmsNotificationAttachment)3 Nullable (androidx.annotation.Nullable)2 HashSet (java.util.HashSet)2 Mention (org.thoughtcrime.securesms.database.model.Mention)2 Context (android.content.Context)1 Cursor (android.database.Cursor)1 TextUtils (android.text.TextUtils)1 NonNull (androidx.annotation.NonNull)1 Collectors (com.annimon.stream.Collectors)1 Stream (com.annimon.stream.Stream)1 NotificationInd (com.google.android.mms.pdu_alt.NotificationInd)1 PduHeaders (com.google.android.mms.pdu_alt.PduHeaders)1 InvalidProtocolBufferException (com.google.protobuf.InvalidProtocolBufferException)1 IOException (java.io.IOException)1 SecureRandom (java.security.SecureRandom)1