use of org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage in project Signal-Android by WhisperSystems.
the class MmsDatabase method getOutgoingMessage.
@Override
public OutgoingMediaMessage getOutgoingMessage(long messageId) throws MmsException, NoSuchMessageException {
AttachmentDatabase attachmentDatabase = SignalDatabase.attachments();
MentionDatabase mentionDatabase = SignalDatabase.mentions();
Cursor cursor = null;
try {
cursor = rawQuery(RAW_ID_WHERE, new String[] { String.valueOf(messageId) });
if (cursor != null && cursor.moveToNext()) {
List<DatabaseAttachment> associatedAttachments = attachmentDatabase.getAttachmentsForMessage(messageId);
List<Mention> mentions = mentionDatabase.getMentionsForMessage(messageId);
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String body = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
boolean viewOnce = cursor.getLong(cursor.getColumnIndexOrThrow(VIEW_ONCE)) == 1;
long recipientId = cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = SignalDatabase.threads().getDistributionType(threadId);
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_ID));
long quoteAuthor = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR));
String quoteText = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_BODY));
boolean quoteMissing = cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE_MISSING)) == 1;
List<Attachment> quoteAttachments = Stream.of(associatedAttachments).filter(Attachment::isQuote).map(a -> (Attachment) a).toList();
List<Mention> quoteMentions = parseQuoteMentions(context, cursor);
List<Contact> contacts = getSharedContacts(cursor, associatedAttachments);
Set<Attachment> contactAttachments = new HashSet<>(Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList());
List<LinkPreview> previews = getLinkPreviews(cursor, associatedAttachments);
Set<Attachment> previewAttachments = Stream.of(previews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).collect(Collectors.toSet());
List<Attachment> attachments = Stream.of(associatedAttachments).filterNot(Attachment::isQuote).filterNot(contactAttachments::contains).filterNot(previewAttachments::contains).sorted(new DatabaseAttachment.DisplayOrderComparator()).map(a -> (Attachment) a).toList();
Recipient recipient = Recipient.resolved(RecipientId.from(recipientId));
Set<NetworkFailure> networkFailures = new HashSet<>();
Set<IdentityKeyMismatch> mismatches = new HashSet<>();
QuoteModel quote = null;
if (quoteId > 0 && quoteAuthor > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) {
quote = new QuoteModel(quoteId, RecipientId.from(quoteAuthor), quoteText, quoteMissing, quoteAttachments, quoteMentions);
}
if (!TextUtils.isEmpty(mismatchDocument)) {
try {
mismatches = JsonUtils.fromJson(mismatchDocument, IdentityKeyMismatchSet.class).getItems();
} catch (IOException e) {
Log.w(TAG, e);
}
}
if (!TextUtils.isEmpty(networkDocument)) {
try {
networkFailures = JsonUtils.fromJson(networkDocument, NetworkFailureSet.class).getItems();
} catch (IOException e) {
Log.w(TAG, e);
}
}
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupUpdateMessage(recipient, new MessageGroupContext(body, Types.isGroupV2(outboxType)), attachments, timestamp, 0, false, quote, contacts, previews, mentions);
} else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
}
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, viewOnce, distributionType, quote, contacts, previews, mentions, networkFailures, mismatches);
if (Types.isSecureType(outboxType)) {
return new OutgoingSecureMediaMessage(message);
}
return message;
}
throw new NoSuchMessageException("No record found for id: " + messageId);
} catch (IOException e) {
throw new MmsException(e);
} finally {
if (cursor != null)
cursor.close();
}
}
use of org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage 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;
}
use of org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage in project Signal-Android by WhisperSystems.
the class GroupV1MessageProcessor method storeMessage.
@Nullable
private static Long storeMessage(@NonNull Context context, @NonNull SignalServiceContent content, @NonNull SignalServiceGroup group, @NonNull GroupContext storage, boolean outgoing) {
if (group.getAvatar().isPresent()) {
ApplicationDependencies.getJobManager().add(new AvatarGroupsV1DownloadJob(GroupId.v1orThrow(group.getGroupId())));
}
try {
if (outgoing) {
MessageDatabase mmsDatabase = SignalDatabase.mms();
RecipientId recipientId = SignalDatabase.recipients().getOrInsertFromGroupId(GroupId.v1orThrow(group.getGroupId()));
Recipient recipient = Recipient.resolved(recipientId);
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
mmsDatabase.markAsSent(messageId, true);
return threadId;
} else {
MessageDatabase smsDatabase = SignalDatabase.sms();
String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalHighTrustPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerReceivedTimestamp(), System.currentTimeMillis(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt(), content.getServerUuid());
IncomingGroupUpdateMessage groupMessage = new IncomingGroupUpdateMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
if (insertResult.isPresent()) {
ApplicationDependencies.getMessageNotifier().updateNotification(context, insertResult.get().getThreadId());
return insertResult.get().getThreadId();
} else {
return null;
}
}
} catch (MmsException e) {
Log.w(TAG, e);
}
return null;
}
use of org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage in project Signal-Android by WhisperSystems.
the class GroupManagerV1 method sendGroupUpdate.
private static GroupActionResult sendGroupUpdate(@NonNull Context context, @NonNull GroupId.V1 groupId, @NonNull Set<RecipientId> members, @Nullable String groupName, @Nullable byte[] avatar, int newMemberCount) {
Attachment avatarAttachment = null;
RecipientId groupRecipientId = SignalDatabase.recipients().getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
List<GroupContext.Member> uuidMembers = new ArrayList<>(members.size());
List<String> e164Members = new ArrayList<>(members.size());
for (RecipientId member : members) {
Recipient recipient = Recipient.resolved(member);
if (recipient.hasE164()) {
e164Members.add(recipient.requireE164());
uuidMembers.add(GroupV1MessageProcessor.createMember(recipient.requireE164()));
}
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder().setId(ByteString.copyFrom(groupId.getDecodedId())).setType(GroupContext.Type.UPDATE).addAllMembersE164(e164Members).addAllMembers(uuidMembers);
if (groupName != null)
groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) {
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, false, false, null, null, null, null, null);
}
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null, null);
return new GroupActionResult(groupRecipient, threadId, newMemberCount, Collections.emptyList());
}
use of org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage in project Signal-Android by signalapp.
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;
}
Aggregations