From 471e028cf31dd5ac4e1cd00a5d5e982500c8b4d4 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 16 Mar 2021 14:56:47 +1100 Subject: [PATCH] implementation of the receiving side of Data Extraction notifications & explicit group updates notifications --- .../securesms/database/MmsSmsColumns.java | 36 ++++++++++++ .../securesms/database/Storage.kt | 56 ++++++++++++++----- .../database/model/DisplayRecord.java | 16 ++++++ .../database/model/MessageRecord.java | 30 ++++++++-- .../securesms/jobs/PushDecryptJob.java | 4 +- .../service/ExpiringMessageManager.java | 1 + app/src/main/res/values/strings.xml | 12 ++++ .../libsession/messaging/StorageProtocol.kt | 4 ++ .../control/DataExtractionNotification.kt | 6 +- .../messages/signal/IncomingMediaMessage.java | 34 ++++++----- .../MessageReceiverHandler.kt | 25 +++++++-- .../DataExtractionNotificationInfoMessage.kt | 16 ++++++ .../api/messages/SignalServiceGroup.java | 6 +- 13 files changed, 206 insertions(+), 40 deletions(-) create mode 100644 libsession/src/main/java/org/session/libsession/messaging/sending_receiving/dataextraction/DataExtractionNotificationInfoMessage.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java index 36b86023e..d26fc12b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -70,6 +70,10 @@ public interface MmsSmsColumns { protected static final long GROUP_UPDATE_BIT = 0x10000; protected static final long GROUP_QUIT_BIT = 0x20000; protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000; + protected static final long GROUP_CREATION_BIT = 0x80000; + protected static final long GROUP_NAME_UPDATE_BIT = 0x16000; + protected static final long GROUP_MEMBER_ADDED_BIT = 0x32000; + protected static final long GROUP_MEMBER_REMOVED_BIT = 0x64000; // Encrypted Storage Information XXX public static final long ENCRYPTION_MASK = 0xFF000000; @@ -84,6 +88,8 @@ public interface MmsSmsColumns { // Loki protected static final long ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT = 0x01000000; protected static final long ENCRYPTION_LOKI_SESSION_RESTORE_DONE_BIT = 0x00100000; + protected static final long DATA_EXTRACTION_SCREENSHOT_BIT = 0x00010000; + protected static final long DATA_EXTRACTION_MEDIA_SAVED_BIT = 0x00001000; public static boolean isDraftMessageType(long type) { return (type & BASE_TYPE_MASK) == BASE_DRAFT_TYPE; @@ -197,6 +203,16 @@ public interface MmsSmsColumns { return (type & EXPIRATION_TIMER_UPDATE_BIT) != 0; } + // DATA EXTRACTION NOTIFICATION + + public static boolean isDataExtractionScreenshotUpdate(long type) { + return (type & DATA_EXTRACTION_SCREENSHOT_BIT) != 0; + } + + public static boolean isDataExtractionMediaSavedUpdate(long type) { + return (type & DATA_EXTRACTION_MEDIA_SAVED_BIT) != 0; + } + public static boolean isIncomingCall(long type) { return type == INCOMING_CALL_TYPE; } @@ -209,14 +225,34 @@ public interface MmsSmsColumns { return type == MISSED_CALL_TYPE; } + // GROUPS EXPLICIT UPDATES + public static boolean isGroupUpdate(long type) { return (type & GROUP_UPDATE_BIT) != 0; } + public static boolean isGroupCreation(long type) { + return (type & GROUP_CREATION_BIT) != 0; + } + + public static boolean isGroupNameUpdate(long type) { + return (type & GROUP_NAME_UPDATE_BIT) != 0; + } + + public static boolean isGroupMemberAdded(long type) { + return (type & GROUP_MEMBER_ADDED_BIT) != 0; + } + + public static boolean isGroupMemberRemoved(long type) { + return (type & GROUP_MEMBER_REMOVED_BIT) != 0; + } + public static boolean isGroupQuit(long type) { return (type & GROUP_QUIT_BIT) != 0; } + + public static boolean isFailedDecryptType(long type) { return (type & ENCRYPTION_REMOTE_FAILED_BIT) != 0; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 6a0d42777..57c6a3410 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -8,18 +8,23 @@ import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.Job import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.MessageSendJob +import org.session.libsession.messaging.messages.signal.* import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.threads.Address +import org.session.libsession.messaging.threads.Address.Companion.fromSerialized import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.utilities.GroupUtil +import org.session.libsession.utilities.IdentityKeyUtil import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.preferences.ProfileKeyUtil import org.session.libsignal.libsignal.ecc.ECKeyPair import org.session.libsignal.libsignal.util.KeyHelper import org.session.libsignal.libsignal.util.guava.Optional @@ -29,21 +34,13 @@ import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.loki.api.opengroups.PublicChat import org.session.libsignal.utilities.logging.Log -import org.session.libsession.utilities.IdentityKeyUtil import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities import org.thoughtcrime.securesms.loki.utilities.get import org.thoughtcrime.securesms.loki.utilities.getString -import org.session.libsession.messaging.messages.signal.IncomingMediaMessage -import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage -import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage import org.thoughtcrime.securesms.mms.PartAuthority -import org.session.libsession.messaging.messages.signal.IncomingGroupMessage -import org.session.libsession.messaging.messages.signal.IncomingTextMessage -import org.session.libsession.messaging.messages.signal.OutgoingTextMessage -import org.session.libsession.utilities.preferences.ProfileKeyUtil class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { override fun getUserPublicKey(): String? { @@ -125,17 +122,21 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, PointerAttachment.forPointer(Optional.of(it)).orNull() } val mediaMessage = OutgoingMediaMessage.from(message, Recipient.from(context, targetAddress, false), attachments, quote.orNull(), linkPreviews.orNull().firstOrNull()) - mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) + mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID + ?: -1, message.sentTimestamp!!) } else { // It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment val attachments: Optional> = Optional.of(message.attachmentIDs.mapNotNull { DatabaseFactory.getAttachmentProvider(context).getSignalAttachmentPointer(it) }) - val mediaMessage = IncomingMediaMessage.from(message, senderAddress, senderRecipient.expireMessages * 1000L, group, attachments, quote, linkPreviews) + // FIXME deal with DataExtraction parameter + val mediaMessage = IncomingMediaMessage.from(message, senderAddress, senderRecipient.expireMessages * 1000L, group, attachments, quote, linkPreviews, Optional.absent()) if (group.isPresent) { - mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) + mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID + ?: -1, message.sentTimestamp!!) } else { - mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1) + mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID + ?: -1) } } if (insertResult.isPresent) { @@ -157,7 +158,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } } val textMessage = OutgoingTextMessage.from(message, Recipient.from(context, targetAddress, false)) - smsDatabase.insertMessageOutbox(message.threadID ?: -1, textMessage, message.sentTimestamp!!) + smsDatabase.insertMessageOutbox(message.threadID + ?: -1, textMessage, message.sentTimestamp!!) } else { val textMessage = IncomingTextMessage.from(message, senderAddress, group, senderRecipient.expireMessages * 1000L) if (group.isPresent) { @@ -417,7 +419,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, sentTimestamp, 0, null, listOf(), listOf()) val mmsDB = DatabaseFactory.getMmsDatabase(context) val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context) - if (mmsSmsDB.getMessageFor(sentTimestamp,userPublicKey) != null) return + if (mmsSmsDB.getMessageFor(sentTimestamp, userPublicKey) != null) return val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null) mmsDB.markAsSent(infoMessageID, true) } @@ -538,4 +540,30 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, override fun getAttachmentThumbnailUri(attachmentId: AttachmentId): Uri { return PartAuthority.getAttachmentThumbnailUri(attachmentId) } + + // Data Extraction Notification + override fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, groupID: String?, sentTimestamp: Long) { + val database = DatabaseFactory.getMmsDatabase(context) + val address = fromSerialized(senderPublicKey) + val recipient = Recipient.from(context, address, false) + + if (recipient.isBlocked) return + + var groupInfo = Optional.absent() + if (groupID != null) { + groupInfo = Optional.of(SignalServiceGroup(groupID.toByteArray(), SignalServiceGroup.GroupType.SIGNAL)) + } + val mediaMessage = IncomingMediaMessage(address, sentTimestamp, -1, + 0, false, + false, + Optional.absent(), + groupInfo, + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.of(message)) + + database.insertSecureDecryptedMessageInbox(mediaMessage, -1) + } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java index ab40f3381..cf712351e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java @@ -113,6 +113,14 @@ public abstract class DisplayRecord { return SmsDatabase.Types.isGroupUpdate(type); } + public boolean isGroupCreation() { return MmsSmsColumns.Types.isGroupCreation(type); } + + public boolean isGroupNameUpdate() { return MmsSmsColumns.Types.isGroupNameUpdate(type); } + + public boolean isGroupMemberAdded() { return MmsSmsColumns.Types.isGroupMemberAdded(type); } + + public boolean isGroupMemberRemoved() { return MmsSmsColumns.Types.isGroupMemberRemoved(type); } + public boolean isGroupQuit() { return SmsDatabase.Types.isGroupQuit(type); } @@ -125,6 +133,14 @@ public abstract class DisplayRecord { return SmsDatabase.Types.isExpirationTimerUpdate(type); } + public boolean isDataExtractionScreenshot() { + return MmsSmsColumns.Types.isDataExtractionScreenshotUpdate(type); + } + + public boolean isDataExtractionMediaSaved() { + return MmsSmsColumns.Types.isDataExtractionMediaSavedUpdate(type); + } + public boolean isCallLog() { return SmsDatabase.Types.isCallLog(type); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index b41e4ae78..a043e7483 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -90,10 +90,26 @@ public abstract class MessageRecord extends DisplayRecord { @Override public SpannableString getDisplayBody(@NonNull Context context) { - if (isGroupUpdate() && isOutgoing()) { + if (isGroupCreation() && isOutgoing()) { + return new SpannableString(context.getString(R.string.MessageRecord_you_created_a_new_group)); + } else if (isGroupCreation()) { + return new SpannableString(context.getString(R.string.MessageRecord_s_created_a_new_group, getIndividualRecipient().toShortString())); + } else if (isGroupUpdate() && isOutgoing()) { return new SpannableString(context.getString(R.string.MessageRecord_you_updated_group)); } else if (isGroupUpdate()) { return new SpannableString(GroupDescription.Companion.getDescription(context, getBody()).toString(getIndividualRecipient())); + } else if (isGroupNameUpdate() && isOutgoing()) { + return new SpannableString(context.getString(R.string.MessageRecord_you_renamed_the_group)); + } else if (isGroupNameUpdate()) { + return new SpannableString(context.getString(R.string.MessageRecord_s_renamed_the_group, getIndividualRecipient().toShortString())); + } else if (isGroupMemberAdded() && isOutgoing()) { + return new SpannableString(context.getString(R.string.MessageRecord_you_added_new_members_to_the_group)); + } else if (isGroupMemberAdded()) { + return new SpannableString(context.getString(R.string.MessageRecord_s_added_new_members_to_the_group, getIndividualRecipient().toShortString())); + } else if (isGroupMemberRemoved() && isOutgoing()) { + return new SpannableString(context.getString(R.string.MessageRecord_you_added_new_members_to_the_group)); + } else if (isGroupMemberRemoved()) { + return new SpannableString(context.getString(R.string.MessageRecord_s_removed_members_from_the_group, getIndividualRecipient().toShortString())); } else if (isGroupQuit() && isOutgoing()) { return new SpannableString(context.getString(R.string.MessageRecord_left_group)); } else if (isGroupQuit()) { @@ -107,14 +123,20 @@ public abstract class MessageRecord extends DisplayRecord { } else if (isJoined()) { return new SpannableString(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().toShortString())); } else if (isExpirationTimerUpdate()) { - int seconds = (int)(getExpiresIn() / 1000); + int seconds = (int) (getExpiresIn() / 1000); if (seconds <= 0) { return isOutgoing() ? new SpannableString(context.getString(R.string.MessageRecord_you_disabled_disappearing_messages)) - : new SpannableString(context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, getIndividualRecipient().toShortString())); + : new SpannableString(context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, getIndividualRecipient().toShortString())); } String time = ExpirationUtil.getExpirationDisplayValue(context, seconds); return isOutgoing() ? new SpannableString(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time)) - : new SpannableString(context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, getIndividualRecipient().toShortString(), time)); + : new SpannableString(context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, getIndividualRecipient().toShortString(), time)); + } else if (isDataExtractionScreenshot()) { + return isOutgoing() ? new SpannableString(context.getString(R.string.MessageRecord_you_took_a_screenshot)) + : new SpannableString(context.getString(R.string.MessageRecord_s_took_a_screenshot, getIndividualRecipient().toShortString())); + } else if (isDataExtractionMediaSaved()) { + return isOutgoing() ? new SpannableString(context.getString(R.string.MessageRecord_media_saved_by_you)) + : new SpannableString(context.getString(R.string.MessageRecord_media_saved_by_s, getIndividualRecipient().toShortString())); } else if (isIdentityUpdate()) { return new SpannableString(context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, getIndividualRecipient().toShortString())); } else if (isIdentityVerified()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 6ab83939b..46f99eae9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -272,6 +272,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { Optional.absent(), Optional.absent(), Optional.absent(), + Optional.absent(), Optional.absent()); database.insertSecureDecryptedMessageInbox(mediaMessage, -1); @@ -371,9 +372,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } } else { + // FIXME handle DataExtraction parameter below where Optional.absent() is IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1, message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(), - quote, sharedContacts, linkPreviews); + quote, sharedContacts, linkPreviews, Optional.absent()); MmsDatabase database = DatabaseFactory.getMmsDatabase(context); database.beginTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java index 77eee247c..646f6756e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -86,6 +86,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM Optional.absent(), Optional.absent(), Optional.absent(), + Optional.absent(), Optional.absent()); database.insertSecureDecryptedMessageInbox(mediaMessage, -1); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34ab7d89b..2d3b0ff67 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -493,6 +493,14 @@ Received a message encrypted using an old version of Session that is no longer supported. Please ask the sender to update to the most recent version and resend the message. You have left the group. You updated the group. + You created a new group. + %1$s created a new group. + You renamed the group. + %1$s renamed the group. + You added new members to the group. + %1$s added new members to the group. + You removed members from the group. + %1$s removed members from the group. You called Contact called Missed call @@ -505,6 +513,10 @@ %1$s disabled disappearing messages. You set the disappearing message timer to %1$s %1$s set the disappearing message timer to %2$s + You took a screenshot. + %1$s took a screenshot. + Media saved by you. + Media saved by %1$s. Your safety number with %s has changed. You marked your safety number with %s verified You marked your safety number with %s verified from another device diff --git a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt index 2c54c3838..dceee60ac 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -11,6 +11,7 @@ import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.opengroups.OpenGroup import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.threads.Address @@ -152,4 +153,7 @@ interface StorageProtocol { // Message Handling /// Returns the ID of the `TSIncomingMessage` that was constructed. fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?): Long? + + // Data Extraction Notification + fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, groupID: String?, sentTimestamp: Long) } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/DataExtractionNotification.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/DataExtractionNotification.kt index a612584af..e963a8e1e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/DataExtractionNotification.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/DataExtractionNotification.kt @@ -12,7 +12,7 @@ class DataExtractionNotification(): ControlMessage() { // Kind enum sealed class Kind { class Screenshot() : Kind() - class MediaSaved(val timestanp: Long) : Kind() + class MediaSaved(val timestamp: Long) : Kind() val description: String = run { when(this) { @@ -50,7 +50,7 @@ class DataExtractionNotification(): ControlMessage() { val kind = kind ?: return false return when(kind) { is Kind.Screenshot -> true - is Kind.MediaSaved -> kind.timestanp > 0 + is Kind.MediaSaved -> kind.timestamp > 0 } } @@ -66,7 +66,7 @@ class DataExtractionNotification(): ControlMessage() { is Kind.Screenshot -> dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT is Kind.MediaSaved -> { dataExtractionNotification.type = SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED - dataExtractionNotification.timestamp = kind.timestanp + dataExtractionNotification.timestamp = kind.timestamp } } val contentProto = SignalServiceProtos.Content.newBuilder() diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.java index aa330ce22..8fbc0ef88 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.java @@ -3,6 +3,7 @@ package org.session.libsession.messaging.messages.signal; import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment; +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage; import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact; import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview; @@ -26,9 +27,11 @@ public class IncomingMediaMessage { private final int subscriptionId; private final long expiresIn; private final boolean expirationUpdate; - private final QuoteModel quote; private final boolean unidentified; + private final DataExtractionNotificationInfoMessage dataExtractionNotification; + private final QuoteModel quote; + private final List attachments = new LinkedList<>(); private final List sharedContacts = new LinkedList<>(); private final List linkPreviews = new LinkedList<>(); @@ -44,17 +47,19 @@ public class IncomingMediaMessage { Optional> attachments, Optional quote, Optional> sharedContacts, - Optional> linkPreviews) + Optional> linkPreviews, + Optional dataExtractionNotification) { - this.push = true; - this.from = from; - this.sentTimeMillis = sentTimeMillis; - this.body = body.orNull(); - this.subscriptionId = subscriptionId; - this.expiresIn = expiresIn; - this.expirationUpdate = expirationUpdate; - this.quote = quote.orNull(); - this.unidentified = unidentified; + this.push = true; + this.from = from; + this.sentTimeMillis = sentTimeMillis; + this.body = body.orNull(); + this.subscriptionId = subscriptionId; + this.expiresIn = expiresIn; + this.expirationUpdate = expirationUpdate; + this.dataExtractionNotification = dataExtractionNotification.orNull(); + this.quote = quote.orNull(); + this.unidentified = unidentified; if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.INSTANCE.getEncodedId(group.get())); else this.groupId = null; @@ -70,10 +75,11 @@ public class IncomingMediaMessage { Optional group, Optional> attachments, Optional quote, - Optional> linkPreviews) + Optional> linkPreviews, + Optional dataExtractionNotification) { return new IncomingMediaMessage(from, message.getReceivedTimestamp(), -1, expiresIn, false, - false, Optional.fromNullable(message.getText()), group, attachments, quote, Optional.absent(), linkPreviews); + false, Optional.fromNullable(message.getText()), group, attachments, quote, Optional.absent(), linkPreviews, dataExtractionNotification); } public int getSubscriptionId() { @@ -128,6 +134,8 @@ public class IncomingMediaMessage { return linkPreviews; } + public DataExtractionNotificationInfoMessage getDataExtractionNotification() { return dataExtractionNotification; } + public boolean isUnidentified() { return unidentified; } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index 114b0ba32..1f0a130d0 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -9,6 +9,7 @@ import org.session.libsession.messaging.messages.control.* import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.sending_receiving.attachments.PointerAttachment +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel @@ -43,6 +44,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content, is TypingIndicator -> handleTypingIndicator(message) is ClosedGroupControlMessage -> handleClosedGroupControlMessage(message) is ExpirationTimerUpdate -> handleExpirationTimerUpdate(message, proto) + is DataExtractionNotification -> handleDataExtractionNotification(message) is ConfigurationMessage -> handleConfigurationMessage(message) is VisibleMessage -> handleVisibleMessage(message, proto, openGroupID) } @@ -102,6 +104,21 @@ fun MessageReceiver.disableExpirationTimer(message: ExpirationTimerUpdate, proto SSKEnvironment.shared.messageExpirationManager.disableExpirationTimer(id, senderPublicKey, proto) } +// Data Extraction Notification handling + +private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) { + val storage = MessagingConfiguration.shared.storage + val senderPublicKey = message.sender!! + val notification: DataExtractionNotificationInfoMessage = when(message.kind) { + is DataExtractionNotification.Kind.Screenshot -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) + is DataExtractionNotification.Kind.MediaSaved -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.MEDIASAVED) + else -> return + } + storage.insertDataExtractionNotificationMessage(senderPublicKey, notification, message.groupPublicKey, message.sentTimestamp!!) +} + +// Configuration message handling + private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMessage) { val context = MessagingConfiguration.shared.context val storage = MessagingConfiguration.shared.storage @@ -239,7 +256,7 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }), null, null, LinkedList(admins.map { Address.fromSerialized(it) }), formationTimestamp) // Notify the user - storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) + storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.NEW, name, members, admins, sentTimestamp) } storage.setProfileSharing(Address.fromSerialized(groupID), true) // Add the group to the user's set of public keys to poll for @@ -356,7 +373,7 @@ private fun MessageReceiver.handleClosedGroupNameChanged(message: ClosedGroupCon val name = kind.name storage.updateTitle(groupID, name) - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.NAME_UPDATE, name, members, admins, message.sentTimestamp!!) } private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupControlMessage) { @@ -384,7 +401,7 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) storage.insertOutgoingInfoMessage(context, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, name, members, admins, threadID, message.sentTimestamp!!) } else { - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.MEMBER_ADDED, name, members, admins, message.sentTimestamp!!) } if (userPublicKey in admins) { // send current encryption key to the latest added members @@ -445,7 +462,7 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup } val (contextType, signalType) = if (senderLeft) SignalServiceProtos.GroupContext.Type.QUIT to SignalServiceGroup.Type.QUIT - else SignalServiceProtos.GroupContext.Type.UPDATE to SignalServiceGroup.Type.UPDATE + else SignalServiceProtos.GroupContext.Type.UPDATE to SignalServiceGroup.Type.MEMBER_REMOVED storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, message.sentTimestamp!!) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/dataextraction/DataExtractionNotificationInfoMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/dataextraction/DataExtractionNotificationInfoMessage.kt new file mode 100644 index 000000000..64d7af7ff --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/dataextraction/DataExtractionNotificationInfoMessage.kt @@ -0,0 +1,16 @@ +package org.session.libsession.messaging.sending_receiving.dataextraction + +class DataExtractionNotificationInfoMessage { + + enum class Kind { + SCREENSHOT, + MEDIASAVED + } + + var kind: Kind? = null + + constructor(kind: Kind?) { + this.kind = kind + } + +} \ No newline at end of file diff --git a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java index b50353109..fa84a389f 100644 --- a/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java +++ b/libsignal/src/main/java/org/session/libsignal/service/api/messages/SignalServiceGroup.java @@ -37,7 +37,11 @@ public class SignalServiceGroup { UPDATE, DELIVER, QUIT, - REQUEST_INFO + REQUEST_INFO, + NEW, + NAME_UPDATE, + MEMBER_ADDED, + MEMBER_REMOVED } private final byte[] groupId;