From cd6c838953b9b593bdbc0533e5f8be4b3b0e2e2c Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 15 Mar 2021 16:22:25 +1100 Subject: [PATCH 01/36] clean --- .../sskenvironment/DataExtractionNotificationManager.kt | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/sskenvironment/DataExtractionNotificationManager.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/DataExtractionNotificationManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/DataExtractionNotificationManager.kt deleted file mode 100644 index 0c8fa779d4..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/DataExtractionNotificationManager.kt +++ /dev/null @@ -1,4 +0,0 @@ -package org.thoughtcrime.securesms.sskenvironment - -class DataExtractionNotificationManager { -} \ No newline at end of file From 471e028cf31dd5ac4e1cd00a5d5e982500c8b4d4 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 16 Mar 2021 14:56:47 +1100 Subject: [PATCH 02/36] 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 36b86023e6..d26fc12b21 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 6a0d42777d..57c6a34100 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 ab40f3381c..cf712351e1 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 b41e4ae78e..a043e74830 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 6ab83939bf..46f99eae9c 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 77eee247cd..646f6756e6 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 34ab7d89b4..2d3b0ff674 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 2c54c38387..dceee60ac5 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 a612584af8..e963a8e1ee 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 aa330ce227..8fbc0ef884 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 114b0ba326..1f0a130d09 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 0000000000..64d7af7ff1 --- /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 b503531097..fa84a389f0 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; From 9d82b4b7a90905d60634fb3e98df44acb8e081e1 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 17 Mar 2021 14:26:29 +1100 Subject: [PATCH 03/36] sending media saved notification --- .../org/thoughtcrime/securesms/MediaPreviewActivity.java | 9 +++++++++ .../securesms/conversation/ConversationFragment.java | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 6399dbc022..3308b0f992 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -53,6 +53,8 @@ import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; +import org.session.libsession.messaging.messages.control.DataExtractionNotification; +import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment; import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.recipients.Recipient; @@ -353,11 +355,18 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im saveTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); + // Sending a Data extraction notification + sendMediaSavedNotificationIfNeeded(); }) .execute(); }); } + private void sendMediaSavedNotificationIfNeeded() { + DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); + MessageSender.send(message, conversationRecipient.getAddress()); + } + @SuppressLint("StaticFieldLeak") private void deleteMedia() { MediaItem mediaItem = getCurrentMediaItem(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 0d30b8ce97..4bf0cf905f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -57,6 +57,7 @@ import androidx.recyclerview.widget.RecyclerView.OnScrollListener; import com.annimon.stream.Stream; +import org.session.libsession.messaging.messages.control.DataExtractionNotification; import org.session.libsession.messaging.messages.visible.Quote; import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.opengroups.OpenGroupAPI; @@ -745,6 +746,8 @@ public class ConversationFragment extends Fragment if (!Util.isEmpty(attachments)) { SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); + // Sending a Data extraction notification + sendMediaSavedNotificationIfNeeded(); return; } @@ -757,6 +760,11 @@ public class ConversationFragment extends Fragment }); } + private void sendMediaSavedNotificationIfNeeded() { + DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); + MessageSender.send(message, recipient.getAddress()); + } + @Override public @NonNull Loader onCreateLoader(int id, Bundle args) { Log.i(TAG, "onCreateLoader"); From 2ca8b1acb1b34a181a23a524b0a7d0f88b6b5f87 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 17 Mar 2021 15:08:09 +1100 Subject: [PATCH 04/36] missed case for sending media saved notification --- .../securesms/MediaOverviewActivity.java | 12 ++++++++++++ .../thoughtcrime/securesms/MediaPreviewActivity.java | 3 +++ .../securesms/conversation/ConversationFragment.java | 3 +++ 3 files changed, 18 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index 0056718e3c..39e817037b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -52,6 +52,8 @@ import androidx.viewpager.widget.ViewPager; import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager; import com.google.android.material.tabs.TabLayout; +import org.session.libsession.messaging.messages.control.DataExtractionNotification; +import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.threads.Address; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.MediaDatabase; @@ -351,6 +353,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()])); actionMode.finish(); + // Sending a Data extraction notification + sendMediaSavedNotificationIfNeeded(); } }.execute(); }) @@ -358,6 +362,14 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { }, mediaRecords.size()); } + /** + * Send a MediaSaved notification to the recipient + */ + private void sendMediaSavedNotificationIfNeeded() { + DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); + MessageSender.send(message, recipient.getAddress()); + } + @SuppressLint("StaticFieldLeak") private void handleDeleteMedia(@NonNull Collection mediaRecords) { int recordCount = mediaRecords.size(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 3308b0f992..5775ce936c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -362,6 +362,9 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im }); } + /** + * Send a MediaSaved notification to the recipient + */ private void sendMediaSavedNotificationIfNeeded() { DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); MessageSender.send(message, conversationRecipient.getAddress()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 4bf0cf905f..b833ad6c31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -760,6 +760,9 @@ public class ConversationFragment extends Fragment }); } + /** + * Send a MediaSaved notification to the recipient + */ private void sendMediaSavedNotificationIfNeeded() { DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); MessageSender.send(message, recipient.getAddress()); From ddede475b4b3e6d0a55eb27fc889813283694579 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 19 Mar 2021 16:25:00 +1100 Subject: [PATCH 05/36] Start of Update messages builder class implementation & usage for Input group updates --- .../securesms/database/Storage.kt | 9 ++- .../loki/protocol/ClosedGroupsProtocolV2.kt | 14 ++-- app/src/main/res/values/strings.xml | 8 ++ .../libsession/messaging/StorageProtocol.kt | 1 + .../MessageReceiverHandler.kt | 10 +-- .../utilities/UpdateMessageBuilder.kt | 74 +++++++++++++++++++ libsession/src/main/res/values/strings.xml | 8 ++ .../api/messages/SignalServiceGroup.java | 6 +- 8 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt 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 3a5fe5147f..03d86dd627 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -19,6 +19,7 @@ import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.recipients.Recipient +import org.session.libsession.messaging.utilities.UpdateMessageBuilder import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.libsignal.ecc.ECKeyPair @@ -79,6 +80,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return recipient.profileKey } + override fun getDisplayNameForRecipient(recipientPublicKey: String): String? { + val database = DatabaseFactory.getLokiUserDatabase(context) + return database.getDisplayName(recipientPublicKey) + } + override fun getOrGenerateRegistrationID(): Int { var registrationID = TextSecurePreferences.getLocalRegistrationId(context) if (registrationID == null) { @@ -397,7 +403,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, .addAllAdmins(admins) val group = SignalServiceGroup(type1, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) - val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "") + val messageBody = UpdateMessageBuilder.buildGroupUpdateMessage(context, group, senderPublicKey) + val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), messageBody) val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index 9fba4b66ef..9aa4ddd389 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -33,6 +33,7 @@ import org.session.libsignal.utilities.Hex import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.recipients.Recipient +import org.session.libsession.messaging.utilities.UpdateMessageBuilder import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences @@ -361,7 +362,7 @@ object ClosedGroupsProtocolV2 { apiDB.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey) // Notify the user (if we didn't make the group) if (userPublicKey != senderPublicKey) { - insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) + insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.NEW_GROUP, name, members, admins, sentTimestamp) } else if (prevGroup == null) { // only notify if we created this group val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) @@ -415,12 +416,12 @@ object ClosedGroupsProtocolV2 { } val (contextType, signalType) = if (senderLeft) GroupContext.Type.QUIT to SignalServiceGroup.Type.QUIT - else GroupContext.Type.UPDATE to SignalServiceGroup.Type.UPDATE + else GroupContext.Type.UPDATE to SignalServiceGroup.Type.MEMBER_REMOVED if (userPublicKey == senderPublicKey) { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) insertOutgoingInfoMessage(context, groupID, contextType, name, members, admins, threadID, sentTimestamp) } else { - insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, sentTimestamp) + insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, updateMembers, admins, sentTimestamp) } } @@ -450,7 +451,7 @@ object ClosedGroupsProtocolV2 { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp) } else { - insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) + insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.MEMBER_ADDED, name, updateMembers, admins, sentTimestamp) } if (userPublicKey in admins) { // send current encryption key to the latest added members @@ -489,7 +490,7 @@ object ClosedGroupsProtocolV2 { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp) } else { - insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) + insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.NAME_CHANGE, name, members, admins, sentTimestamp) } } @@ -608,7 +609,8 @@ object ClosedGroupsProtocolV2 { .addAllAdmins(admins) val group = SignalServiceGroup(type1, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true) - val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "") + val messageBody = UpdateMessageBuilder.buildGroupUpdateMessage(context, group, senderPublicKey) + val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), messageBody) val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ff4e7f611..09c7b0f1be 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 added you to the group. + You renamed the group to %1$s + %1$s renamed the group to: %2$s + You added %1$s to the group. + %1$s added %2$s to the group. + You removed %1$s from the group. + %1$s removed %2$s from the group. You called Contact called Missed call 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 e3062f73ee..5c66b2bd06 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -34,6 +34,7 @@ interface StorageProtocol { fun getUserProfilePictureURL(): String? fun getProfileKeyForRecipient(recipientPublicKey: String): ByteArray? + fun getDisplayNameForRecipient(recipientPublicKey: String): String? // Signal Protocol 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 530badd329..740c181798 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 @@ -239,7 +239,7 @@ private fun handleNewClosedGroup(sender: String, groupPublicKey: String, name: S 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) + storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.NEW_GROUP, name, members, admins) } storage.setProfileSharing(Address.fromSerialized(groupID), true) // Add the group to the user's set of public keys to poll for @@ -356,7 +356,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) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.NAME_CHANGE, name, members, admins) } private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupControlMessage) { @@ -388,7 +388,7 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo MessageSender.sendLatestEncryptionKeyPair(member, groupPublicKey) } } - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.MEMBER_ADDED, name, updateMembers, admins) } private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroupControlMessage) { @@ -435,9 +435,9 @@ 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) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, updateMembers, admins) } private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupControlMessage) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt new file mode 100644 index 0000000000..bcc0d4761b --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -0,0 +1,74 @@ +package org.session.libsession.messaging.utilities + +import android.content.Context +import org.session.libsession.R +import org.session.libsession.messaging.MessagingConfiguration +import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.service.internal.push.SignalServiceProtos + +object UpdateMessageBuilder { + + fun buildGroupUpdateMessage(context: Context, groupInfo: SignalServiceGroup, sender: String, isOutgoing: Boolean = false): String { + val updateType = groupInfo.type + val senderName: String = if (!isOutgoing) { + MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender) ?: sender + } else { sender } + var message: String = "" + when (updateType) { + SignalServiceGroup.Type.NEW_GROUP -> { + message = if (isOutgoing) { + context.getString(R.string.MessageRecord_you_created_a_new_group) + } else { + context.getString(R.string.MessageRecord_s_added_you_to_the_group, senderName) + } + } + SignalServiceGroup.Type.NAME_CHANGE -> { + message = if (isOutgoing) { + context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, groupInfo.name.get()) + } else { + context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, groupInfo.name.get()) + } + } + SignalServiceGroup.Type.MEMBER_ADDED -> { + val members = groupInfo.members.get().joinToString(", ") { + MessagingConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it + } + message = if (isOutgoing) { + context.getString(R.string.MessageRecord_you_added_s_to_the_group, members) + } else { + context.getString(R.string.MessageRecord_s_added_s_to_the_group, senderName, members) + } + } + SignalServiceGroup.Type.MEMBER_REMOVED -> { + val members = groupInfo.members.get().joinToString(", ") { + MessagingConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it + } + message = if (isOutgoing) { + context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members) + } else { + context.getString(R.string.MessageRecord_s_removed_s_from_the_group, senderName, members) + } + } + SignalServiceGroup.Type.QUIT -> { + message = if (isOutgoing) { + context.getString(R.string.MessageRecord_left_group) + } else { + context.getString(R.string.ConversationItem_group_action_left, senderName) + } + } + else -> { + message = context.getString(R.string.MessageRecord_s_updated_group, senderName) + } + } + return message + } + + fun buildExpirationTimerMessage(): String { + return "" + } + + fun buildDataExtractionMessage(): String { + return "" + } + +} \ No newline at end of file diff --git a/libsession/src/main/res/values/strings.xml b/libsession/src/main/res/values/strings.xml index 70b424bd22..ea6a33b70b 100644 --- a/libsession/src/main/res/values/strings.xml +++ b/libsession/src/main/res/values/strings.xml @@ -487,6 +487,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 added you to the group. + You renamed the group to %1$s + %1$s renamed the group to: %2$s + You added %1$s to the group. + %1$s added %2$s to the group. + You removed %1$s from the group. + %1$s removed %2$s from the group. You called Contact called Missed call 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 b503531097..68856a87cd 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_GROUP, + NAME_CHANGE, + MEMBER_ADDED, + MEMBER_REMOVED } private final byte[] groupId; From 5dcb3d77d4468be50b571641a1d939f5c31a13ff Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 19 Mar 2021 16:39:41 +1100 Subject: [PATCH 06/36] revert changes regarding explicit group updates as it's now in a separate PR --- .../securesms/database/MmsSmsColumns.java | 36 ------------------- .../database/model/DisplayRecord.java | 16 --------- .../database/model/MessageRecord.java | 26 +------------- app/src/main/res/values/strings.xml | 8 ----- .../MessageReceiverHandler.kt | 8 ++--- .../api/messages/SignalServiceGroup.java | 6 +--- 6 files changed, 6 insertions(+), 94 deletions(-) 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 d26fc12b21..36b86023e6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -70,10 +70,6 @@ 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; @@ -88,8 +84,6 @@ 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; @@ -203,16 +197,6 @@ 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; } @@ -225,34 +209,14 @@ 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/model/DisplayRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java index cf712351e1..ab40f3381c 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,14 +113,6 @@ 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); } @@ -133,14 +125,6 @@ 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 a043e74830..8ac7fac42f 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,26 +90,8 @@ public abstract class MessageRecord extends DisplayRecord { @Override public SpannableString getDisplayBody(@NonNull Context context) { - 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()) { + if (isGroupUpdate() && isOutgoing()) { 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()) { @@ -131,12 +113,6 @@ public abstract class MessageRecord extends DisplayRecord { 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)); - } 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/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2d3b0ff674..166f8c42d1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -493,14 +493,6 @@ 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 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 1f0a130d09..64a4e39c62 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 @@ -256,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.NEW, name, members, admins, sentTimestamp) + storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) } storage.setProfileSharing(Address.fromSerialized(groupID), true) // Add the group to the user's set of public keys to poll for @@ -373,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.NAME_UPDATE, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, message.sentTimestamp!!) } private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupControlMessage) { @@ -401,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.MEMBER_ADDED, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, message.sentTimestamp!!) } if (userPublicKey in admins) { // send current encryption key to the latest added members @@ -462,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.MEMBER_REMOVED + else SignalServiceProtos.GroupContext.Type.UPDATE to SignalServiceGroup.Type.UPDATE storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, message.sentTimestamp!!) } 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 fa84a389f0..b503531097 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,11 +37,7 @@ public class SignalServiceGroup { UPDATE, DELIVER, QUIT, - REQUEST_INFO, - NEW, - NAME_UPDATE, - MEMBER_ADDED, - MEMBER_REMOVED + REQUEST_INFO } private final byte[] groupId; From a47113f2c5e0ff3c378452397b56c4b860c5bde8 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 19 Mar 2021 16:57:00 +1100 Subject: [PATCH 07/36] revert changes --- .../securesms/database/model/MessageRecord.java | 8 +++++--- .../libsession/messaging/jobs/MessageReceiveJob.kt | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) 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 8ac7fac42f..b41e4ae78e 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 @@ -91,6 +91,8 @@ public abstract class MessageRecord extends DisplayRecord { @Override public SpannableString getDisplayBody(@NonNull Context context) { 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 (isGroupQuit() && isOutgoing()) { return new SpannableString(context.getString(R.string.MessageRecord_left_group)); @@ -105,14 +107,14 @@ 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 (isIdentityUpdate()) { return new SpannableString(context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, getIndividualRecipient().toShortString())); } else if (isIdentityVerified()) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt index 014b66a0c8..4ce46b4577 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt @@ -15,7 +15,7 @@ class MessageReceiveJob(val data: ByteArray, val isBackgroundPoll: Boolean, val // Settings override val maxFailureCount: Int = 10 companion object { - val TAG = MessageReceiveJob::class.qualifiedName + val TAG = MessageReceiveJob::class.simpleName val KEY: String = "MessageReceiveJob" //keys used for database storage purpose From 28cecc02363f2cb9bcefcd9570e72c16b35d42fc Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 23 Mar 2021 09:58:17 +1100 Subject: [PATCH 08/36] expiration timer messages generation updated --- .../conversation/ConversationActivity.java | 4 +- .../conversation/ConversationUpdateItem.java | 2 +- .../securesms/database/SmsDatabase.java | 2 +- .../loki/utilities/GroupDescription.kt | 1 + .../service/ExpiringMessageManager.java | 4 +- app/src/main/res/values/strings.xml | 1 + .../OutgoingExpirationUpdateMessage.java | 8 +-- .../utilities/UpdateMessageBuilder.kt | 54 ++++++++++++++----- libsession/src/main/res/values/strings.xml | 1 + 9 files changed, 56 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index ca14153ed3..e5bb1b85ba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -89,6 +89,7 @@ import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate; import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.threads.DistributionTypes; +import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.MediaTypes; import org.session.libsignal.libsignal.InvalidMessageException; @@ -808,7 +809,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime); ExpirationTimerUpdate message = new ExpirationTimerUpdate(expirationTime); message.setSentTimestamp(System.currentTimeMillis()); - OutgoingExpirationUpdateMessage outgoingMessage = OutgoingExpirationUpdateMessage.from(message, recipient); + String displayedMessage = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(getApplicationContext(), expirationTime, null, false); + OutgoingExpirationUpdateMessage outgoingMessage = OutgoingExpirationUpdateMessage.from(message, recipient, displayedMessage); try { message.setId(DatabaseFactory.getMmsDatabase(ConversationActivity.this).insertMessageOutbox(outgoingMessage, getAllocatedThreadId(ConversationActivity.this), false, null)); MessageSender.send(message, recipient.getAddress()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 6b76c0572a..7efd5ca4e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -175,7 +175,7 @@ public class ConversationUpdateItem extends LinearLayout icon.setImageResource(R.drawable.ic_group_grey600_24dp); icon.clearColorFilter(); - GroupDescription.Companion.getDescription(getContext(), messageRecord.getBody()).addListener(this); + GroupDescription.Companion.getDescription(getContext(), messageRecord.getBody()).addListener(this); //TODO Brice: could be removed if GroupDescription is removed body.setText(messageRecord.getDisplayBody(getContext())); title.setVisibility(GONE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 300a5c55f8..28dbbbe420 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -390,7 +390,7 @@ public class SmsDatabase extends MessagingDatabase { values.put(REPLY_PATH_PRESENT, message.isReplyPathPresent()); values.put(SERVICE_CENTER, message.getServiceCenterAddress()); - values.put(BODY, message.getMessageBody()); + values.put(BODY, message.getMessageBody()); //TODO Brice: is that encoded? values.put(TYPE, type); values.put(THREAD_ID, threadId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt index 62d48aa524..dd642beb59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt @@ -13,6 +13,7 @@ import network.loki.messenger.R import org.session.libsignal.utilities.logging.Log import java.io.IOException +//TODO Brice: that class should be deprecated class GroupDescription(context: Context, groupContext: SignalServiceProtos.GroupContext?) { private val context: Context private val groupContext: SignalServiceProtos.GroupContext? 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 77eee247cd..5a60e291c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.recipients.Recipient; +import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.utilities.SSKEnvironment; import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.service.api.messages.SignalServiceGroup; @@ -78,10 +79,11 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM GroupContext groupContext = content.getDataMessage().getGroup(); groupInfo = Optional.of(new SignalServiceGroup(groupContext.getId().toByteArray(), SignalServiceGroup.GroupType.SIGNAL)); } + String updateMessage = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, duration, senderPublicKey, false); IncomingMediaMessage mediaMessage = new IncomingMediaMessage(address, content.getDataMessage().getTimestamp(), -1, duration * 1000L, true, false, - Optional.absent(), + Optional.of(updateMessage), groupInfo, Optional.absent(), Optional.absent(), diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3c707cd5c7..ac5121c1d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -501,6 +501,7 @@ %1$s added %2$s to the group. You removed %1$s from the group. %1$s removed %2$s from the group. + You were removed from the group. You called Contact called Missed call diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java index 77996ba11e..3fac6292f2 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java @@ -10,15 +10,15 @@ import java.util.LinkedList; public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage { - public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn) { - super(recipient, "", new LinkedList(), sentTimeMillis, + public OutgoingExpirationUpdateMessage(Recipient recipient, String body, long sentTimeMillis, long expiresIn) { + super(recipient, body, new LinkedList(), sentTimeMillis, DistributionTypes.CONVERSATION, expiresIn, null, Collections.emptyList(), Collections.emptyList()); } public static OutgoingExpirationUpdateMessage from(ExpirationTimerUpdate message, - Recipient recipient) { - return new OutgoingExpirationUpdateMessage(recipient, message.getSentTimestamp(), message.getDuration() * 1000); + Recipient recipient, String body) { + return new OutgoingExpirationUpdateMessage(recipient, body, message.getSentTimestamp(), message.getDuration() * 1000); } @Override diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index bcc0d4761b..367e549c02 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -1,19 +1,23 @@ package org.session.libsession.messaging.utilities import android.content.Context +import android.text.SpannableString import org.session.libsession.R import org.session.libsession.messaging.MessagingConfiguration +import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate +import org.session.libsession.utilities.ExpirationUtil import org.session.libsignal.service.api.messages.SignalServiceGroup -import org.session.libsignal.service.internal.push.SignalServiceProtos object UpdateMessageBuilder { - fun buildGroupUpdateMessage(context: Context, groupInfo: SignalServiceGroup, sender: String, isOutgoing: Boolean = false): String { + fun buildGroupUpdateMessage(context: Context, groupInfo: SignalServiceGroup, sender: String? = null, isOutgoing: Boolean = false): String { val updateType = groupInfo.type - val senderName: String = if (!isOutgoing) { - MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender) ?: sender - } else { sender } var message: String = "" + if (!isOutgoing && sender == null) return message + val senderName: String? = if (!isOutgoing) { + MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender + } else { sender } + when (updateType) { SignalServiceGroup.Type.NEW_GROUP -> { message = if (isOutgoing) { @@ -40,13 +44,25 @@ object UpdateMessageBuilder { } } SignalServiceGroup.Type.MEMBER_REMOVED -> { - val members = groupInfo.members.get().joinToString(", ") { - MessagingConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it - } - message = if (isOutgoing) { - context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members) + val storage = MessagingConfiguration.shared.storage + val userPublicKey = storage.getUserPublicKey()!! + // 1st case: you are part of the removed members + message = if (userPublicKey in groupInfo.members.get()) { + if (isOutgoing) { + context.getString(R.string.MessageRecord_left_group) + } else { + context.getString(R.string.MessageRecord_you_were_removed_from_the_group) + } } else { - context.getString(R.string.MessageRecord_s_removed_s_from_the_group, senderName, members) + // 2nd case: you are not part of the removed members + val members = groupInfo.members.get().joinToString(", ") { + storage.getDisplayNameForRecipient(it) ?: it + } + if (isOutgoing) { + context.getString(R.string.MessageRecord_you_removed_s_from_the_group, members) + } else { + context.getString(R.string.MessageRecord_s_removed_s_from_the_group, senderName, members) + } } } SignalServiceGroup.Type.QUIT -> { @@ -63,8 +79,20 @@ object UpdateMessageBuilder { return message } - fun buildExpirationTimerMessage(): String { - return "" + fun buildExpirationTimerMessage(context: Context, duration: Int, sender: String? = null, isOutgoing: Boolean = false): String { + val seconds = (duration!! / 1000) + if (!isOutgoing && sender == null) return "" + val senderName: String? = if (!isOutgoing) { + MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender + } else { sender } + return if (seconds <= 0) { + if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages) + else context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, senderName) + } else { + val time = ExpirationUtil.getExpirationDisplayValue(context, seconds) + if (isOutgoing)context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time) + else context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, senderName, time) + } } fun buildDataExtractionMessage(): String { diff --git a/libsession/src/main/res/values/strings.xml b/libsession/src/main/res/values/strings.xml index ea6a33b70b..90cbb92869 100644 --- a/libsession/src/main/res/values/strings.xml +++ b/libsession/src/main/res/values/strings.xml @@ -495,6 +495,7 @@ %1$s added %2$s to the group. You removed %1$s from the group. %1$s removed %2$s from the group. + You were removed from the group. You called Contact called Missed call From 68a3c73ab1e672057a27c600fa768568d850797b Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 23 Mar 2021 10:59:03 +1100 Subject: [PATCH 09/36] clean --- .../main/java/org/thoughtcrime/securesms/database/Storage.kt | 2 -- 1 file changed, 2 deletions(-) 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 edffc8cac0..cb7c8b7aee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -44,8 +44,6 @@ 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 -import org.session.libsignal.service.loki.utilities.prettifiedDescription class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { override fun getUserPublicKey(): String? { From 1b01d0afd47ef4f64dee97cc9868393c1277f14b Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 23 Mar 2021 11:03:09 +1100 Subject: [PATCH 10/36] clean --- .../main/java/org/thoughtcrime/securesms/database/Storage.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 cb7c8b7aee..d6515ae80c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -24,7 +24,6 @@ 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 @@ -44,6 +43,8 @@ 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 +import org.session.libsignal.service.loki.utilities.prettifiedDescription class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { override fun getUserPublicKey(): String? { From cc24e29cb2d5cb3cad4f1817f18cc734bdf383ab Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 8 Apr 2021 15:11:46 +1000 Subject: [PATCH 11/36] clean --- .../java/org/session/libsession/messaging/StorageProtocol.kt | 2 +- .../messaging/messages/control/DataExtractionNotification.kt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) 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 5b88e362a7..3327952304 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -162,4 +162,4 @@ interface StorageProtocol { // 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 040ea66d0f..538b0b0001 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 @@ -73,5 +73,4 @@ class DataExtractionNotification(): ControlMessage() { return null } } - -} \ No newline at end of file +} From 8df7d2bb49137e3f45b23f455653c67905577616 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 8 Apr 2021 15:27:25 +1000 Subject: [PATCH 12/36] clean --- .../java/org/thoughtcrime/securesms/database/SmsDatabase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 1a7326f4c9..d5c34c5c54 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -390,7 +390,7 @@ public class SmsDatabase extends MessagingDatabase { values.put(REPLY_PATH_PRESENT, message.isReplyPathPresent()); values.put(SERVICE_CENTER, message.getServiceCenterAddress()); - values.put(BODY, message.getMessageBody()); //TODO Brice: is that encoded? + values.put(BODY, message.getMessageBody()); values.put(TYPE, type); values.put(THREAD_ID, threadId); From fce1d60d7d31d352bce6415b982a12c17ffa8aa8 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 8 Apr 2021 15:40:00 +1000 Subject: [PATCH 13/36] clean --- .../main/java/org/thoughtcrime/securesms/database/Storage.kt | 3 +-- .../messaging/messages/signal/IncomingMediaMessage.java | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) 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 ab88d16ce0..57aad5b10d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -148,8 +148,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val signalServiceAttachments = attachments.mapNotNull { it.toSignalPointer() } - //TODO deal with data extraction instead of Optional.absent() - val mediaMessage = IncomingMediaMessage.from(message, senderAddress, targetRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews, Optional.absent()) + val mediaMessage = IncomingMediaMessage.from(message, senderAddress, targetRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews) mmsDatabase.beginTransaction() mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.receivedTimestamp ?: 0) } 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 dff60354c3..ad110964a0 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 @@ -75,11 +75,10 @@ public class IncomingMediaMessage { Optional group, List attachments, Optional quote, - Optional> linkPreviews, - Optional dataExtractionNotification) + Optional> linkPreviews) { return new IncomingMediaMessage(from, message.getSentTimestamp(), -1, expiresIn, false, - false, Optional.fromNullable(message.getText()), group, Optional.fromNullable(attachments), quote, Optional.absent(), linkPreviews, dataExtractionNotification); + false, Optional.fromNullable(message.getText()), group, Optional.fromNullable(attachments), quote, Optional.absent(), linkPreviews, Optional.absent()); } public int getSubscriptionId() { From c03b49eeb732697f0b8f637b8aaaa441de03419d Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 8 Apr 2021 15:48:00 +1000 Subject: [PATCH 14/36] clean --- .../java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java | 1 - 1 file changed, 1 deletion(-) 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 0bf78d38ce..ad2e55dafc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -373,7 +373,6 @@ 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, Optional.absent()); From e57c697eca8193349fc34560c27d5e5ce01935a5 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 9 Apr 2021 16:06:12 +1000 Subject: [PATCH 15/36] finishing data extraction handling --- .../securesms/MediaOverviewActivity.java | 2 ++ .../securesms/MediaPreviewActivity.java | 2 ++ .../conversation/ConversationFragment.java | 2 ++ .../conversation/ConversationUpdateItem.java | 19 +++++++++++++++++++ .../securesms/database/MmsDatabase.java | 8 ++++++++ .../securesms/database/MmsSmsColumns.java | 12 ++++++++++++ .../securesms/database/Storage.kt | 8 ++------ .../database/model/DisplayRecord.java | 14 ++++++++++++++ .../database/model/MessageRecord.java | 5 ++++- .../database/model/ThreadRecord.java | 6 +++++- app/src/main/res/values/strings.xml | 4 ++-- .../libsession/messaging/StorageProtocol.kt | 2 +- .../messages/signal/IncomingMediaMessage.java | 14 ++++++++++++++ .../MessageReceiverHandler.kt | 10 +++++++--- 14 files changed, 94 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index 39e817037b..762d88b81c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -366,6 +366,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { * Send a MediaSaved notification to the recipient */ private void sendMediaSavedNotificationIfNeeded() { + // we don't send media saved notification for groups + if (recipient.isGroupRecipient()) return; DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); MessageSender.send(message, recipient.getAddress()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 39a67d762d..8bc53250a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -368,6 +368,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im * Send a MediaSaved notification to the recipient */ private void sendMediaSavedNotificationIfNeeded() { + // we don't send media saved notification for groups + if (conversationRecipient.isGroupRecipient()) return; DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); MessageSender.send(message, conversationRecipient.getAddress()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index b833ad6c31..d69574c4d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -764,6 +764,8 @@ public class ConversationFragment extends Fragment * Send a MediaSaved notification to the recipient */ private void sendMediaSavedNotificationIfNeeded() { + // we don't send media saved notification for groups + if (recipient.isGroupRecipient()) return; DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(System.currentTimeMillis())); MessageSender.send(message, recipient.getAddress()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 6b76c0572a..0bdb8ba3cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -15,6 +15,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage; import org.thoughtcrime.securesms.BindableConversationItem; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; @@ -106,6 +107,8 @@ public class ConversationUpdateItem extends LinearLayout else if (messageRecord.isCallLog()) setCallRecord(messageRecord); else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord); + else if (messageRecord.isScreenshotExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT); + else if (messageRecord.isMediaSavedExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.MEDIASAVED); else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord); else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord); else if (messageRecord.isIdentityVerified() || @@ -149,6 +152,22 @@ public class ConversationUpdateItem extends LinearLayout date.setVisibility(GONE); } + private void setDataExtractionRecord(final MessageRecord messageRecord, DataExtractionNotificationInfoMessage.Kind kind) { + @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme()); + if (kind == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) { + icon.setImageResource(R.drawable.quick_camera_dark); + } else if (kind == DataExtractionNotificationInfoMessage.Kind.MEDIASAVED) { + icon.setImageResource(R.drawable.ic_file_download_white_36dp); + } + icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + + body.setText(messageRecord.getDisplayBody(getContext())); + + title.setVisibility(VISIBLE); + body.setVisibility(VISIBLE); + date.setVisibility(GONE); + } + private void setIdentityRecord(final MessageRecord messageRecord) { icon.setImageResource(R.drawable.ic_security_white_24dp); icon.setColorFilter(new PorterDuffColorFilter(Color.parseColor("#757575"), PorterDuff.Mode.MULTIPLY)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 62de9499a1..12e5ba2d95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -709,6 +709,14 @@ public class MmsDatabase extends MessagingDatabase { type |= Types.EXPIRATION_TIMER_UPDATE_BIT; } + if (retrieved.isScreenshotDataExtraction()) { + type |= Types.SCREENSHOT_EXTRACTION_BIT; + } + + if (retrieved.isMediaSavedDataExtraction()) { + type |= Types.MEDIA_SAVED_EXTRACTION_BIT; + } + return insertMessageInbox(retrieved, "", threadId, type, serverTimestamp); } 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 36b86023e6..aefd1af808 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsColumns.java @@ -71,6 +71,10 @@ public interface MmsSmsColumns { protected static final long GROUP_QUIT_BIT = 0x20000; protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000; + // Data Extraction Information + protected static final long MEDIA_SAVED_EXTRACTION_BIT = 0x01000; + protected static final long SCREENSHOT_EXTRACTION_BIT = 0x02000; + // Encrypted Storage Information XXX public static final long ENCRYPTION_MASK = 0xFF000000; // public static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; Deprecated @@ -197,6 +201,14 @@ public interface MmsSmsColumns { return (type & EXPIRATION_TIMER_UPDATE_BIT) != 0; } + public static boolean isMediaSavedExtraction(long type) { + return (type & MEDIA_SAVED_EXTRACTION_BIT) != 0; + } + + public static boolean isScreenshotExtraction(long type) { + return (type & SCREENSHOT_EXTRACTION_BIT) != 0; + } + public static boolean isIncomingCall(long type) { return type == INCOMING_CALL_TYPE; } 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 57aad5b10d..7722840a49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -581,22 +581,18 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, } // Data Extraction Notification - override fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, groupID: String?, sentTimestamp: Long) { + override fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, 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(), 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 ab40f3381c..28eae16f16 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 @@ -125,6 +125,20 @@ public abstract class DisplayRecord { return SmsDatabase.Types.isExpirationTimerUpdate(type); } + // Data extraction + + public boolean isMediaSavedExtraction() { + return MmsSmsColumns.Types.isMediaSavedExtraction(type); + } + + public boolean isScreenshotExtraction() { + return MmsSmsColumns.Types.isScreenshotExtraction(type); + } + + public boolean isDataExtraction() { + return isMediaSavedExtraction() || isScreenshotExtraction(); + } + 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 b41e4ae78e..64229e95b5 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 @@ -98,6 +98,9 @@ public abstract class MessageRecord extends DisplayRecord { return new SpannableString(context.getString(R.string.MessageRecord_left_group)); } else if (isGroupQuit()) { return new SpannableString(context.getString(R.string.ConversationItem_group_action_left, getIndividualRecipient().toShortString())); + } else if (isDataExtraction()) { + if (isMediaSavedExtraction()) return new SpannableString(context.getString(R.string.MessageRecord_media_saved_by_s, getIndividualRecipient().toShortString())); + if (isScreenshotExtraction()) return new SpannableString(context.getString(R.string.MessageRecord_s_took_a_screenshot, getIndividualRecipient().toShortString())); } else if (isIncomingCall()) { return new SpannableString(context.getString(R.string.MessageRecord_s_called_you, getIndividualRecipient().toShortString())); } else if (isOutgoingCall()) { @@ -175,7 +178,7 @@ public abstract class MessageRecord extends DisplayRecord { } public boolean isUpdate() { - return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() || + return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() || isDataExtraction() || isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isLokiSessionRestoreSent() || isLokiSessionRestoreDone(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index e2a286c854..42c8a20ede 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -103,12 +103,16 @@ public class ThreadRecord extends DisplayRecord { } else if (SmsDatabase.Types.isJoinedType(type)) { return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString())); } else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) { - int seconds = (int)(getExpiresIn() / 1000); + int seconds = (int) (getExpiresIn() / 1000); if (seconds <= 0) { return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_messages_disabled)); } String time = ExpirationUtil.getExpirationDisplayValue(context, seconds); return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time)); + } else if (MmsSmsColumns.Types.isMediaSavedExtraction(type)) { + return emphasisAdded(context.getString(R.string.ThreadRecord_media_saved_by_s, getRecipient().toShortString())); + } else if (MmsSmsColumns.Types.isScreenshotExtraction(type)) { + return emphasisAdded(context.getString(R.string.ThreadRecord_s_took_a_screenshot, getRecipient().toShortString())); } else if (SmsDatabase.Types.isIdentityUpdate(type)) { if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed)); else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString())); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 166f8c42d1..3b0693645a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -505,9 +505,7 @@ %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 @@ -707,6 +705,8 @@ %s is on Session! Disappearing messages disabled Disappearing message time set to %s + %s took a screenshot. + Media saved by %s. Safety number changed Your safety number with %s has changed. You marked verified 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 3327952304..b390637ca2 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -161,5 +161,5 @@ interface StorageProtocol { fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?, attachments: List): Long? // Data Extraction Notification - fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, groupID: String?, sentTimestamp: Long) + fun insertDataExtractionNotificationMessage(senderPublicKey: String, message: DataExtractionNotificationInfoMessage, sentTimestamp: Long) } 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 ad110964a0..53e48a5897 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 @@ -121,6 +121,20 @@ public class IncomingMediaMessage { return groupId != null; } + public boolean isScreenshotDataExtraction() { + if (dataExtractionNotification == null) return false; + else { + return dataExtractionNotification.getKind() == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT; + } + } + + public boolean isMediaSavedDataExtraction() { + if (dataExtractionNotification == null) return false; + else { + return dataExtractionNotification.getKind() == DataExtractionNotificationInfoMessage.Kind.MEDIASAVED; + } + } + public QuoteModel getQuote() { return quote; } 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 2c0e2269c9..190a55195f 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 @@ -96,6 +96,9 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer // Data Extraction Notification handling private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) { + // we don't handle data extraction messages for groups (they shouldn't be sent, but in case we filter them here too) + if (message.groupPublicKey != null) return + val storage = MessagingConfiguration.shared.storage val senderPublicKey = message.sender!! val notification: DataExtractionNotificationInfoMessage = when(message.kind) { @@ -103,7 +106,7 @@ private fun MessageReceiver.handleDataExtractionNotification(message: DataExtrac is DataExtractionNotification.Kind.MediaSaved -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.MEDIASAVED) else -> return } - storage.insertDataExtractionNotificationMessage(senderPublicKey, notification, message.groupPublicKey, message.sentTimestamp!!) + storage.insertDataExtractionNotificationMessage(senderPublicKey, notification, message.sentTimestamp!!) } // Configuration message handling @@ -170,7 +173,8 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS } } // Get or create thread - val threadID = storage.getOrCreateThreadIdFor(message.syncTarget ?: message.sender!!, message.groupPublicKey, openGroupID) + val threadID = storage.getOrCreateThreadIdFor(message.syncTarget + ?: message.sender!!, message.groupPublicKey, openGroupID) // Parse quote if needed var quoteModel: QuoteModel? = null if (message.quote != null && proto.dataMessage.hasQuote()) { @@ -259,7 +263,7 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli storage.updateMembers(groupID, members.map { Address.fromSerialized(it) }) } else { storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }), - null, null, LinkedList(admins.map { Address.fromSerialized(it) }), formationTimestamp) + 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) } From 40015c2898a7ecac819db877d96e9973fd8a62e0 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 9 Apr 2021 16:36:18 +1000 Subject: [PATCH 16/36] clean --- .../messaging/messages/signal/IncomingMediaMessage.java | 2 -- .../dataextraction/DataExtractionNotificationInfoMessage.kt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) 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 53e48a5897..1d359a6c64 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 @@ -147,8 +147,6 @@ 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/dataextraction/DataExtractionNotificationInfoMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/dataextraction/DataExtractionNotificationInfoMessage.kt index 64d7af7ff1..108b51c7aa 100644 --- 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 @@ -13,4 +13,4 @@ class DataExtractionNotificationInfoMessage { this.kind = kind } -} \ No newline at end of file +} From 2bce2738b30b3d15670f325ad6eb501836dead80 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 12 Apr 2021 12:02:41 +1000 Subject: [PATCH 17/36] media saved notification is sent only when incoming media are saved --- .../org/thoughtcrime/securesms/MediaOverviewActivity.java | 7 +++++-- .../org/thoughtcrime/securesms/MediaPreviewActivity.java | 6 ++++-- .../securesms/conversation/ConversationFragment.java | 6 ++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index 762d88b81c..fe6f249b0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -353,8 +353,11 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { saveTask.executeOnExecutor(THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[attachments.size()])); actionMode.finish(); - // Sending a Data extraction notification - sendMediaSavedNotificationIfNeeded(); + // Sending a Data extraction notification (for incoming attachments only) + boolean containsIncoming = mediaRecords.parallelStream().anyMatch(m -> !m.isOutgoing()); + if (containsIncoming) { + sendMediaSavedNotificationIfNeeded(); + } } }.execute(); }) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 8bc53250a4..03e1c6b4f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -357,8 +357,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im saveTask.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); - // Sending a Data extraction notification - sendMediaSavedNotificationIfNeeded(); + // Sending a Data extraction notification (for incoming attachments only) + if(!mediaItem.outgoing) { + sendMediaSavedNotificationIfNeeded(); + } }) .execute(); }); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index d69574c4d1..32668cb247 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -746,8 +746,10 @@ public class ConversationFragment extends Fragment if (!Util.isEmpty(attachments)) { SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity()); saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); - // Sending a Data extraction notification - sendMediaSavedNotificationIfNeeded(); + // Sending a Data extraction notification (for incoming attachments only) + if(!message.isOutgoing()) { + sendMediaSavedNotificationIfNeeded(); + } return; } From 9cdcdc43a604dfcb1d24e66131501fae4c66f56c Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 14 Apr 2021 16:37:04 +1000 Subject: [PATCH 18/36] redesign of group update messages management --- .../conversation/ConversationActivity.java | 18 +++----- .../securesms/database/MmsDatabase.java | 15 ++++--- .../securesms/database/SmsDatabase.java | 3 +- .../securesms/database/Storage.kt | 25 ++++------- .../database/model/MessageRecord.java | 2 + .../loki/protocol/ClosedGroupsProtocolV2.kt | 35 +++++++-------- .../service/ExpiringMessageManager.java | 19 ++++---- .../libsession/messaging/StorageProtocol.kt | 4 +- .../messages/control/ExpirationTimerUpdate.kt | 5 +++ .../messages/signal/IncomingGroupMessage.java | 14 ++---- .../OutgoingExpirationUpdateMessage.java | 7 +-- .../signal/OutgoingGroupMediaMessage.java | 44 +++++-------------- .../MessageReceiverHandler.kt | 35 ++++++++------- .../MessageSenderClosedGroup.kt | 15 ++++--- .../utilities/UpdateMessageBuilder.kt | 15 ++++--- .../api/messages/SignalServiceGroup.java | 2 +- 16 files changed, 117 insertions(+), 141 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index de07e941b3..1582f36227 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -89,7 +89,6 @@ import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate; import org.session.libsession.messaging.messages.visible.VisibleMessage; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.threads.DistributionTypes; -import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.MediaTypes; import org.session.libsignal.libsignal.InvalidMessageException; @@ -159,7 +158,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MmsException; -import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage; import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage; import org.session.libsession.messaging.messages.signal.OutgoingSecureMediaMessage; import org.thoughtcrime.securesms.mms.QuoteId; @@ -176,6 +174,7 @@ import org.session.libsession.messaging.threads.recipients.RecipientModifiedList import org.thoughtcrime.securesms.search.model.MessageResult; import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.messages.signal.OutgoingTextMessage; +import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.MediaUtil; @@ -807,16 +806,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity @Override protected Void doInBackground(Void... params) { DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime); - ExpirationTimerUpdate message = new ExpirationTimerUpdate(null, expirationTime); + ExpirationTimerUpdate message = new ExpirationTimerUpdate(expirationTime); + message.setRecipient(recipient.getAddress().serialize()); // we need the recipient in ExpiringMessageManager.insertOutgoingExpirationTimerMessage message.setSentTimestamp(System.currentTimeMillis()); - String displayedMessage = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(getApplicationContext(), expirationTime, null, false); - OutgoingExpirationUpdateMessage outgoingMessage = OutgoingExpirationUpdateMessage.from(message, recipient, displayedMessage); - try { - message.setId(DatabaseFactory.getMmsDatabase(ConversationActivity.this).insertMessageOutbox(outgoingMessage, getAllocatedThreadId(ConversationActivity.this), false, null)); - MessageSender.send(message, recipient.getAddress()); - } catch (MmsException e) { - Log.w(TAG, e); - } + ExpiringMessageManager expiringMessageManager = ApplicationContext.getInstance(getApplicationContext()).getExpiringMessageManager(); + expiringMessageManager.setExpirationTimer(message); + MessageSender.send(message, recipient.getAddress()); + return null; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 700d64101a..de4248016f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -515,7 +515,7 @@ public class MmsDatabase extends MessagingDatabase { } if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { - return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, quote, contacts, previews); + return new OutgoingGroupMediaMessage(recipient, body, null, attachments, timestamp, 0, quote, contacts, previews); } else if (Types.isExpirationTimerUpdate(outboxType)) { return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn); } @@ -689,8 +689,14 @@ public class MmsDatabase extends MessagingDatabase { { if (threadId == -1) { if(retrieved.isGroup()) { - ByteString decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupContext().getId(); - String groupId = GroupUtil.doubleEncodeGroupID(decodedGroupId.toByteArray()); + String decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupId(); + String groupId; + try { + groupId = GroupUtil.doubleEncodeGroupID(decodedGroupId); + } catch (IOException e) { + Log.e(TAG, "Couldn't encrypt group ID"); + throw new MmsException(e); + } Recipient group = Recipient.from(context, Address.fromSerialized(groupId), false); threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(group); } else { @@ -746,8 +752,7 @@ public class MmsDatabase extends MessagingDatabase { if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; if (message.isGroup()) { - if (((OutgoingGroupMediaMessage)message).isGroupUpdate()) type |= Types.GROUP_UPDATE_BIT; - else if (((OutgoingGroupMediaMessage)message).isGroupQuit()) type |= Types.GROUP_QUIT_BIT; + type |= Types.GROUP_UPDATE_BIT; } if (message.isExpirationUpdate()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index d5c34c5c54..a5affbb569 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -347,8 +347,7 @@ public class SmsDatabase extends MessagingDatabase { type |= Types.SECURE_MESSAGE_BIT; } else if (message.isGroup()) { type |= Types.SECURE_MESSAGE_BIT; - if (((IncomingGroupMessage)message).isUpdate()) type |= Types.GROUP_UPDATE_BIT; - else if (((IncomingGroupMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT; + type |= Types.GROUP_UPDATE_BIT; } if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT; 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 1c6a28b564..30c515e91d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -400,31 +400,22 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members) } - override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: SignalServiceProtos.GroupContext.Type, type1: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, sentTimestamp: Long) { - val groupContextBuilder = SignalServiceProtos.GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID))) - .setType(type0) - .setName(name) - .addAllMembers(members) - .addAllAdmins(admins) - val group = SignalServiceGroup(type1, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) + override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, sentTimestamp: Long) { + val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true) val messageBody = UpdateMessageBuilder.buildGroupUpdateMessage(context, group, senderPublicKey) - val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), messageBody) + val infoMessage = IncomingGroupMessage(m, groupID, messageBody) val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) } - override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceProtos.GroupContext.Type, name: String, members: Collection, admins: Collection, threadID: Long, sentTimestamp: Long) { + override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, threadID: Long, sentTimestamp: Long) { val userPublicKey = getUserPublicKey() val recipient = Recipient.from(context, Address.fromSerialized(groupID), false) - val groupContextBuilder = SignalServiceProtos.GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID))) - .setType(type) - .setName(name) - .addAllMembers(members) - .addAllAdmins(admins) - val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, sentTimestamp, 0, false, null, listOf(), listOf()) + + val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) + val messageBody = UpdateMessageBuilder.buildGroupUpdateMessage(context, group, null, true) + val infoMessage = OutgoingGroupMediaMessage(recipient, messageBody, groupID, null, sentTimestamp, 0, false, null, listOf(), listOf()) val mmsDB = DatabaseFactory.getMmsDatabase(context) val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context) if (mmsSmsDB.getMessageFor(sentTimestamp,userPublicKey) != null) return 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 b41e4ae78e..f4c9bb95a7 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 @@ -24,6 +24,8 @@ import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; import network.loki.messenger.R; + +import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.SmsDatabase; import org.session.libsession.database.documents.IdentityKeyMismatch; diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index 6d6f5b4971..df1d7c0b52 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -9,7 +9,6 @@ import org.session.libsignal.libsignal.ecc.ECKeyPair import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage -import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded import org.session.libsignal.service.loki.utilities.toHexString import org.thoughtcrime.securesms.database.DatabaseFactory @@ -26,7 +25,6 @@ import org.session.libsession.messaging.sending_receiving.sendEncryptionKeyPair import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.recipients.Recipient -import org.session.libsession.messaging.utilities.UpdateMessageBuilder import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences @@ -105,11 +103,11 @@ object ClosedGroupsProtocolV2 { apiDB.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey) // Notify the user (if we didn't make the group) if (userPublicKey != senderPublicKey) { - DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.NEW_GROUP, name, members, admins, sentTimestamp) + DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) } else if (prevGroup == null) { // only notify if we created this group val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) - DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp) + DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp) } // Notify the PN server LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) @@ -157,14 +155,14 @@ object ClosedGroupsProtocolV2 { MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, newMembers) } } - val (contextType, signalType) = - if (senderLeft) GroupContext.Type.QUIT to SignalServiceGroup.Type.QUIT - else GroupContext.Type.UPDATE to SignalServiceGroup.Type.MEMBER_REMOVED + val type = + if (senderLeft) SignalServiceGroup.Type.QUIT + else SignalServiceGroup.Type.UPDATE if (userPublicKey == senderPublicKey) { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) - DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, contextType, name, members, admins, threadID, sentTimestamp) + DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type, name, members, admins, threadID, sentTimestamp) } else { - DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, sentTimestamp) + DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, admins, sentTimestamp) } } @@ -192,9 +190,9 @@ object ClosedGroupsProtocolV2 { groupDB.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) }) if (userPublicKey == senderPublicKey) { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) - DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp) + DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp) } else { - DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.MEMBER_ADDED, name, members, admins, sentTimestamp) + DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) } if (userPublicKey in admins) { // send current encryption key to the latest added members @@ -231,9 +229,9 @@ object ClosedGroupsProtocolV2 { // Notify the user if (userPublicKey == senderPublicKey) { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) - DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp) + DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp) } else { - DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.NAME_CHANGE, name, members, admins, sentTimestamp) + DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp) } } @@ -273,9 +271,9 @@ object ClosedGroupsProtocolV2 { // Notify user if (userLeft) { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) - DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, GroupContext.Type.QUIT, name, members, admins, threadID, sentTimestamp) + DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, threadID, sentTimestamp) } else { - DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.QUIT, SignalServiceGroup.Type.QUIT, name, members, admins, sentTimestamp) + DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, sentTimestamp) } } @@ -386,14 +384,13 @@ object ClosedGroupsProtocolV2 { } // Notify the user val wasSenderRemoved = !members.contains(senderPublicKey) - val type0 = if (wasSenderRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE - val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE + val type = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE val admins = group.admins.map { it.toString() } if (userPublicKey == senderPublicKey) { val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID) - DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type0, name, members, admins, threadID, sentTimestamp) + DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type, name, members, admins, threadID, sentTimestamp) } else { - DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins, sentTimestamp) + DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, admins, sentTimestamp) } } // endregion 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 8cc1224d79..8d10605fc8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -78,8 +78,8 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM String senderPublicKey = message.getSender(); // Notify the user - if (userPublicKey.equals(senderPublicKey)) { - // sender is a linked device + if (senderPublicKey == null || userPublicKey.equals(senderPublicKey)) { + // sender is self or a linked device insertOutgoingExpirationTimerMessage(message); } else { insertIncomingExpirationTimerMessage(message); @@ -97,9 +97,10 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM Long sentTimestamp = message.getSentTimestamp(); String groupId = message.getGroupPublicKey(); int duration = message.getDuration(); + String messageBody = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, duration, senderPublicKey, false); Optional groupInfo = Optional.absent(); - Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey); + Address address = Address.fromSerialized(senderPublicKey); Recipient recipient = Recipient.from(context, address, false); // if the sender is blocked, we don't display the update, except if it's in a closed group @@ -117,7 +118,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM IncomingMediaMessage mediaMessage = new IncomingMediaMessage(address, sentTimestamp, -1, duration * 1000L, true, false, - Optional.absent(), + Optional.of(messageBody), groupInfo, Optional.absent(), Optional.absent(), @@ -137,27 +138,25 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM private void insertOutgoingExpirationTimerMessage(ExpirationTimerUpdate message) { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - String senderPublicKey = message.getSender(); Long sentTimestamp = message.getSentTimestamp(); String groupId = message.getGroupPublicKey(); int duration = message.getDuration(); + String messageBody = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, duration, null, true); - Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : senderPublicKey); + Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : message.getRecipient()); Recipient recipient = Recipient.from(context, address, false); try { if (groupId != null) { // conversation is a closed group - GroupContext groupContext = SignalServiceProtos.GroupContext.newBuilder() - .setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupId))).build(); - OutgoingGroupMediaMessage infoMessage = new OutgoingGroupMediaMessage(recipient, groupContext, null, sentTimestamp, duration * 1000L, true, null, Collections.emptyList(), Collections.emptyList()); + OutgoingGroupMediaMessage infoMessage = new OutgoingGroupMediaMessage(recipient, messageBody, groupId, null, sentTimestamp, duration * 1000L, true, null, Collections.emptyList(), Collections.emptyList()); database.insertSecureDecryptedMessageOutbox(infoMessage, -1, sentTimestamp); // we need the group ID as recipient for setExpireMessages below recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(groupId)), false); } else { // conversation is a 1-1 OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipient, - null, + messageBody, Collections.emptyList(), message.getSentTimestamp(), -1, 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 d4aaf2b2cb..d7d5624e24 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -117,9 +117,9 @@ interface StorageProtocol { fun removeClosedGroupPublicKey(groupPublicKey: String) fun addClosedGroupEncryptionKeyPair(encryptionKeyPair: ECKeyPair, groupPublicKey: String) fun removeAllClosedGroupEncryptionKeyPairs(groupPublicKey: String) - fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: SignalServiceProtos.GroupContext.Type, type1: SignalServiceGroup.Type, + fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, sentTimestamp: Long) - fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceProtos.GroupContext.Type, name: String, + fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, threadID: Long, sentTimestamp: Long) fun isClosedGroup(publicKey: String): Boolean fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt index 85f34eed51..5dff39072c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt @@ -33,6 +33,11 @@ class ExpirationTimerUpdate() : ControlMessage() { this.duration = duration } + internal constructor(duration: Int) : this() { + this.syncTarget = null + this.duration = duration + } + // validation override fun isValid(): Boolean { if (!super.isValid()) return false diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java index b0a7de08ee..901f151cdf 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java @@ -4,11 +4,11 @@ import static org.session.libsignal.service.internal.push.SignalServiceProtos.Gr public class IncomingGroupMessage extends IncomingTextMessage { - private final GroupContext groupContext; + private final String groupID; - public IncomingGroupMessage(IncomingTextMessage base, GroupContext groupContext, String body) { + public IncomingGroupMessage(IncomingTextMessage base, String groupID, String body) { super(base, body); - this.groupContext = groupContext; + this.groupID = groupID; } @Override @@ -16,12 +16,4 @@ public class IncomingGroupMessage extends IncomingTextMessage { return true; } - public boolean isUpdate() { - return groupContext.getType().getNumber() == GroupContext.Type.UPDATE_VALUE; - } - - public boolean isQuit() { - return groupContext.getType().getNumber() == GroupContext.Type.QUIT_VALUE; - } - } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java index 94f1016b56..087751e2fd 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java @@ -8,17 +8,18 @@ import org.session.libsession.messaging.threads.recipients.Recipient; import java.util.Collections; import java.util.LinkedList; +// TODO this class could be deleted if its usage in MmsDatabase.getOutgoingMessage is replaced by something elsex public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage { - public OutgoingExpirationUpdateMessage(Recipient recipient, String body, long sentTimeMillis, long expiresIn) { + public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn) { super(recipient, "", new LinkedList(), sentTimeMillis, DistributionTypes.CONVERSATION, expiresIn, true, null, Collections.emptyList(), Collections.emptyList()); } public static OutgoingExpirationUpdateMessage from(ExpirationTimerUpdate message, - Recipient recipient, String body) { - return new OutgoingExpirationUpdateMessage(recipient, body, message.getSentTimestamp(), message.getDuration() * 1000); + Recipient recipient) { + return new OutgoingExpirationUpdateMessage(recipient, message.getSentTimestamp(), message.getDuration() * 1000); } @Override diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java index 7f151ae7ee..a726286be6 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java @@ -19,10 +19,11 @@ import java.util.List; public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { - private final GroupContext group; + private final String groupID; public OutgoingGroupMediaMessage(@NonNull Recipient recipient, - @NonNull String encodedGroupContext, + @NonNull String body, + @Nullable String groupId, @NonNull List avatar, long sentTimeMillis, long expiresIn, @@ -31,30 +32,15 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { @NonNull List previews) throws IOException { - super(recipient, encodedGroupContext, avatar, sentTimeMillis, + super(recipient, body, avatar, sentTimeMillis, DistributionTypes.CONVERSATION, expiresIn, false, quote, contacts, previews); - this.group = GroupContext.parseFrom(Base64.decode(encodedGroupContext)); + this.groupID = groupId; } public OutgoingGroupMediaMessage(@NonNull Recipient recipient, - @NonNull GroupContext group, - @Nullable final Attachment avatar, - long expireIn, - @Nullable QuoteModel quote, - @NonNull List contacts, - @NonNull List previews) - { - super(recipient, Base64.encodeBytes(group.toByteArray()), - new LinkedList() {{if (avatar != null) add(avatar);}}, - System.currentTimeMillis(), - DistributionTypes.CONVERSATION, expireIn, false, quote, contacts, previews); - - this.group = group; - } - - public OutgoingGroupMediaMessage(@NonNull Recipient recipient, - @NonNull GroupContext group, + @NonNull String body, + @Nullable String groupId, @Nullable final Attachment avatar, long sentTime, long expireIn, @@ -63,12 +49,12 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { @NonNull List contacts, @NonNull List previews) { - super(recipient, Base64.encodeBytes(group.toByteArray()), + super(recipient, body, new LinkedList() {{if (avatar != null) add(avatar);}}, sentTime, DistributionTypes.CONVERSATION, expireIn, expirationUpdate, quote, contacts, previews); - this.group = group; + this.groupID = groupId; } @Override @@ -76,15 +62,7 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { return true; } - public boolean isGroupUpdate() { - return group.getType().getNumber() == GroupContext.Type.UPDATE_VALUE; - } - - public boolean isGroupQuit() { - return group.getType().getNumber() == GroupContext.Type.QUIT_VALUE; - } - - public GroupContext getGroupContext() { - return group; + public String getGroupId() { + return groupID; } } 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 00415aa9cc..b860f02732 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 @@ -243,8 +243,15 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli } else { storage.createGroup(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }), null, null, LinkedList(admins.map { Address.fromSerialized(it) }), formationTimestamp) + val userPublicKey = TextSecurePreferences.getLocalNumber(context) // Notify the user - storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.NEW_GROUP, name, members, admins, sentTimestamp) + if (userPublicKey == sender) { + // sender is a linked device + val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) + storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, threadID, sentTimestamp) + } else { + storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, sentTimestamp) + } } storage.setProfileSharing(Address.fromSerialized(groupID), true) // Add the group to the user's set of public keys to poll for @@ -304,9 +311,8 @@ private fun MessageReceiver.handleClosedGroupUpdated(message: ClosedGroupControl } // Notify the user val wasSenderRemoved = !members.contains(senderPublicKey) - val type0 = if (wasSenderRemoved) SignalServiceProtos.GroupContext.Type.QUIT else SignalServiceProtos.GroupContext.Type.UPDATE - val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, group.admins.map { it.toString() }, message.sentTimestamp!!) + val type = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.MEMBER_REMOVED + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, group.admins.map { it.toString() }, message.sentTimestamp!!) } private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGroupControlMessage) { @@ -378,9 +384,9 @@ private fun MessageReceiver.handleClosedGroupNameChanged(message: ClosedGroupCon if (userPublicKey == senderPublicKey) { // sender is a linked device val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, name, members, admins, threadID, message.sentTimestamp!!) + storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.NAME_CHANGE, name, members, admins, threadID, message.sentTimestamp!!) } else { - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.NAME_CHANGE, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.NAME_CHANGE, name, members, admins, message.sentTimestamp!!) } } @@ -413,9 +419,9 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo if (userPublicKey == senderPublicKey) { // sender is a linked device val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, name, members, admins, threadID, message.sentTimestamp!!) + storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.MEMBER_ADDED, name, updateMembers, admins, threadID, message.sentTimestamp!!) } else { - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.MEMBER_ADDED, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.MEMBER_ADDED, name, updateMembers, admins, message.sentTimestamp!!) } if (userPublicKey in admins) { // send current encryption key to the latest added members @@ -477,17 +483,16 @@ private fun MessageReceiver.handleClosedGroupMembersRemoved(message: ClosedGroup MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, newMembers) } } - val (contextType, signalType) = - if (senderLeft) SignalServiceProtos.GroupContext.Type.QUIT to SignalServiceGroup.Type.QUIT - else SignalServiceProtos.GroupContext.Type.UPDATE to SignalServiceGroup.Type.MEMBER_REMOVED + val type = if (senderLeft) SignalServiceGroup.Type.QUIT + else SignalServiceGroup.Type.MEMBER_REMOVED // Notify the user if (userPublicKey == senderPublicKey) { // sender is a linked device val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, contextType, name, members, admins, threadID, message.sentTimestamp!!) + storage.insertOutgoingInfoMessage(context, groupID, type, name, updateMembers, admins, threadID, message.sentTimestamp!!) } else { - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, updateMembers, admins, message.sentTimestamp!!) } } @@ -533,9 +538,9 @@ private fun MessageReceiver.handleClosedGroupMemberLeft(message: ClosedGroupCont if (userLeft) { //sender is a linked device val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, SignalServiceProtos.GroupContext.Type.QUIT, name, members, admins, threadID, message.sentTimestamp!!) + storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, threadID, message.sentTimestamp!!) } else { - storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.QUIT, SignalServiceGroup.Type.QUIT, name, members, admins, message.sentTimestamp!!) + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, message.sentTimestamp!!) } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 58f8ffe64c..143fa7fb3f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -15,6 +15,7 @@ import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.libsignal.ecc.Curve import org.session.libsignal.libsignal.ecc.ECKeyPair import org.session.libsignal.libsignal.util.guava.Optional +import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded @@ -60,7 +61,7 @@ fun MessageSender.create(name: String, members: Collection): Promise) send(closedGroupControlMessage, Address.fromSerialized(member)) } // Notify the user - val infoType = SignalServiceProtos.GroupContext.Type.UPDATE + val infoType = SignalServiceGroup.Type.MEMBER_ADDED val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) + storage.insertOutgoingInfoMessage(context, groupID, infoType, name, membersToAdd, admins, threadID, sentTime) } fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List) { @@ -189,9 +190,9 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List { @@ -212,7 +213,7 @@ fun MessageSender.leave(groupPublicKey: String, notifyUser: Boolean = true): Pro storage.setActive(groupID, false) sendNonDurably(closedGroupControlMessage, Address.fromSerialized(groupID)).success { // Notify the user - val infoType = SignalServiceProtos.GroupContext.Type.QUIT + val infoType = SignalServiceGroup.Type.QUIT val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) if (notifyUser) { storage.insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index 367e549c02..2e2163f1ad 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -19,7 +19,7 @@ object UpdateMessageBuilder { } else { sender } when (updateType) { - SignalServiceGroup.Type.NEW_GROUP -> { + SignalServiceGroup.Type.CREATION -> { message = if (isOutgoing) { context.getString(R.string.MessageRecord_you_created_a_new_group) } else { @@ -79,24 +79,29 @@ object UpdateMessageBuilder { return message } - fun buildExpirationTimerMessage(context: Context, duration: Int, sender: String? = null, isOutgoing: Boolean = false): String { - val seconds = (duration!! / 1000) + fun buildExpirationTimerMessage(context: Context, duration: Long, sender: String? = null, isOutgoing: Boolean = false): String { if (!isOutgoing && sender == null) return "" val senderName: String? = if (!isOutgoing) { MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender } else { sender } - return if (seconds <= 0) { + return if (duration <= 0) { if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages) else context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, senderName) } else { - val time = ExpirationUtil.getExpirationDisplayValue(context, seconds) + val time = ExpirationUtil.getExpirationDisplayValue(context, duration.toInt()) if (isOutgoing)context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time) else context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, senderName, time) } } + //TODO one this is merged in fun buildDataExtractionMessage(): String { return "" } + /*TODO retro compatibilite old update messages (MessageRecord) + ThreadRecord to display specific messages? (hard unless we can get the incoming / outgoing messages) + Clean code (comments, logs...) + Delete OutgoingExpirationUpdateMessage (check how its used in MmsDatabase l.520 to save messages and how to do the same when getting messages from db without breaking it) + */ } \ 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 68856a87cd..5115844a12 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 @@ -38,7 +38,7 @@ public class SignalServiceGroup { DELIVER, QUIT, REQUEST_INFO, - NEW_GROUP, + CREATION, NAME_CHANGE, MEMBER_ADDED, MEMBER_REMOVED From abb1db7a7e40c812bcbf4ba646609793a01a59a7 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 15 Apr 2021 14:41:29 +1000 Subject: [PATCH 19/36] new approach in update saving --- .../conversation/ConversationUpdateItem.java | 4 -- .../securesms/database/MmsDatabase.java | 23 ++++----- .../securesms/database/MmsSmsColumns.java | 3 ++ .../securesms/database/SmsDatabase.java | 2 +- .../securesms/database/Storage.kt | 11 +++-- .../database/model/DisplayRecord.java | 8 +++- .../database/model/MessageRecord.java | 40 +++++----------- .../database/model/ThreadRecord.java | 4 +- .../loki/utilities/GroupDescription.kt | 2 +- .../service/ExpiringMessageManager.java | 34 ++----------- .../messages/signal/IncomingGroupMessage.java | 6 ++- .../OutgoingExpirationUpdateMessage.java | 25 ++++++---- .../signal/OutgoingGroupMediaMessage.java | 27 ++++------- .../messages/signal/OutgoingMediaMessage.java | 8 +--- .../signal/OutgoingSecureMediaMessage.java | 3 +- .../utilities/UpdateMessageBuilder.kt | 24 ++++------ .../messaging/utilities/UpdateMessageData.kt | 48 +++++++++++++++++++ 17 files changed, 133 insertions(+), 139 deletions(-) create mode 100644 libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 7efd5ca4e2..46e0bef279 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -14,11 +14,9 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import org.thoughtcrime.securesms.BindableConversationItem; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; -import org.thoughtcrime.securesms.loki.utilities.GroupDescription; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.util.DateUtils; import org.session.libsignal.libsignal.util.guava.Optional; @@ -174,8 +172,6 @@ public class ConversationUpdateItem extends LinearLayout private void setGroupRecord(MessageRecord messageRecord) { icon.setImageResource(R.drawable.ic_group_grey600_24dp); icon.clearColorFilter(); - - GroupDescription.Companion.getDescription(getContext(), messageRecord.getBody()).addListener(this); //TODO Brice: could be removed if GroupDescription is removed body.setText(messageRecord.getDisplayBody(getContext())); title.setVisibility(GONE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index de4248016f..d4a26b6ead 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -514,15 +514,7 @@ public class MmsDatabase extends MessagingDatabase { } } - if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) { - return new OutgoingGroupMediaMessage(recipient, body, null, attachments, timestamp, 0, quote, contacts, previews); - } else if (Types.isExpirationTimerUpdate(outboxType)) { - return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn); - } - - boolean expirationTimer = (outboxType & Types.EXPIRATION_TIMER_UPDATE_BIT) != 0; - - OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, expirationTimer, distributionType, quote, contacts, previews, networkFailures, mismatches); + OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts, previews, networkFailures, mismatches); if (Types.isSecureType(outboxType)) { return new OutgoingSecureMediaMessage(message); @@ -532,8 +524,6 @@ public class MmsDatabase extends MessagingDatabase { } throw new NoSuchMessageException("No record found for id: " + messageId); - } catch (IOException e) { - throw new MmsException(e); } finally { if (cursor != null) cursor.close(); @@ -689,7 +679,12 @@ public class MmsDatabase extends MessagingDatabase { { if (threadId == -1) { if(retrieved.isGroup()) { - String decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupId(); + String decodedGroupId; + if (retrieved instanceof OutgoingExpirationUpdateMessage) { + decodedGroupId = ((OutgoingExpirationUpdateMessage)retrieved).getGroupId(); + } else { + decodedGroupId = ((OutgoingGroupMediaMessage)retrieved).getGroupId(); + } String groupId; try { groupId = GroupUtil.doubleEncodeGroupID(decodedGroupId); @@ -751,8 +746,8 @@ public class MmsDatabase extends MessagingDatabase { if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; - if (message.isGroup()) { - type |= Types.GROUP_UPDATE_BIT; + if (message.isGroup() && message instanceof OutgoingGroupMediaMessage) { + if (((OutgoingGroupMediaMessage)message).isUpdateMessage()) type |= Types.GROUP_UPDATE_MESSAGE_BIT; } if (message.isExpirationUpdate()) { 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 36b86023e6..ae8a470069 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,7 @@ 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_UPDATE_MESSAGE_BIT = 0x80000; // Encrypted Storage Information XXX public static final long ENCRYPTION_MASK = 0xFF000000; @@ -213,6 +214,8 @@ public interface MmsSmsColumns { return (type & GROUP_UPDATE_BIT) != 0; } + public static boolean isGroupUpdateMessage(long type) { return (type & GROUP_UPDATE_MESSAGE_BIT) != 0; } + public static boolean isGroupQuit(long type) { return (type & GROUP_QUIT_BIT) != 0; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index a5affbb569..226645b39c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -347,7 +347,7 @@ public class SmsDatabase extends MessagingDatabase { type |= Types.SECURE_MESSAGE_BIT; } else if (message.isGroup()) { type |= Types.SECURE_MESSAGE_BIT; - type |= Types.GROUP_UPDATE_BIT; + if (((IncomingGroupMessage)message).isUpdateMessage()) type |= Types.GROUP_UPDATE_MESSAGE_BIT; } if (message.isPush()) type |= Types.PUSH_MESSAGE_BIT; 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 30c515e91d..dc2452b51d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -22,6 +22,7 @@ import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.GroupRecord import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.messaging.utilities.UpdateMessageBuilder +import org.session.libsession.messaging.utilities.UpdateMessageData import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.IdentityKeyUtil import org.session.libsession.utilities.TextSecurePreferences @@ -41,6 +42,7 @@ import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities import org.thoughtcrime.securesms.loki.utilities.get import org.thoughtcrime.securesms.loki.utilities.getString import org.thoughtcrime.securesms.mms.PartAuthority +import java.util.* class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol { override fun getUserPublicKey(): String? { @@ -403,8 +405,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, sentTimestamp: Long) { val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true) - val messageBody = UpdateMessageBuilder.buildGroupUpdateMessage(context, group, senderPublicKey) - val infoMessage = IncomingGroupMessage(m, groupID, messageBody) + val updateData = UpdateMessageData.buildGroupUpdate(type, name, members).toJSON() + val infoMessage = IncomingGroupMessage(m, groupID, updateData, true) val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) } @@ -413,9 +415,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val userPublicKey = getUserPublicKey() val recipient = Recipient.from(context, Address.fromSerialized(groupID), false) - val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) - val messageBody = UpdateMessageBuilder.buildGroupUpdateMessage(context, group, null, true) - val infoMessage = OutgoingGroupMediaMessage(recipient, messageBody, groupID, null, sentTimestamp, 0, false, null, listOf(), listOf()) + val updateData = UpdateMessageData.buildGroupUpdate(type, name, members).toJSON() + val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf()) val mmsDB = DatabaseFactory.getMmsDatabase(context) val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context) if (mmsSmsDB.getMessageFor(sentTimestamp,userPublicKey) != null) return 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 ab40f3381c..d3be43d879 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 @@ -109,6 +109,7 @@ public abstract class DisplayRecord { public boolean isLokiSessionRestoreDone() { return SmsDatabase.Types.isLokiSessionRestoreDoneType(type); } + // TODO isGroupUpdate and isGroupQuit are kept for compatibility with old update messages, they can be removed later on public boolean isGroupUpdate() { return SmsDatabase.Types.isGroupUpdate(type); } @@ -117,8 +118,13 @@ public abstract class DisplayRecord { return SmsDatabase.Types.isGroupQuit(type); } + public boolean isGroupUpdateMessage() { + return SmsDatabase.Types.isGroupUpdateMessage(type); + } + + //TODO isGroupAction can be replaced by isGroupUpdateMessage in the code when the 2 functions above are removed public boolean isGroupAction() { - return isGroupUpdate() || isGroupQuit(); + return isGroupUpdate() || isGroupQuit() || isGroupUpdateMessage(); } public boolean isExpirationTimerUpdate() { 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 f4c9bb95a7..631883eb5b 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 @@ -26,14 +26,13 @@ import android.text.style.StyleSpan; import network.loki.messenger.R; import org.session.libsession.messaging.utilities.UpdateMessageBuilder; +import org.session.libsession.messaging.utilities.UpdateMessageData; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.SmsDatabase; import org.session.libsession.database.documents.IdentityKeyMismatch; import org.session.libsession.database.documents.NetworkFailure; import org.session.libsession.messaging.threads.recipients.Recipient; -import org.session.libsession.utilities.ExpirationUtil; -import org.thoughtcrime.securesms.loki.utilities.GroupDescription; import java.util.List; @@ -92,39 +91,22 @@ public abstract class MessageRecord extends DisplayRecord { @Override public SpannableString getDisplayBody(@NonNull Context context) { - if (isGroupUpdate() && isOutgoing()) { + if(isGroupUpdateMessage()) { + UpdateMessageData updateMessageData = UpdateMessageData.Companion.fromJSON(getBody()); + return new SpannableString(UpdateMessageBuilder.INSTANCE.buildGroupUpdateMessage(context, updateMessageData, getIndividualRecipient().getAddress().serialize(), isOutgoing())); + } else if (isExpirationTimerUpdate()) { + int seconds = (int) (getExpiresIn() / 1000); + return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing())); + } + // TODO below lines are left here for compatibility with older group update messages, it can be deleted later on + 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())); + return new SpannableString(context.getString(R.string.MessageRecord_s_updated_group, getIndividualRecipient().toShortString())); } else if (isGroupQuit() && isOutgoing()) { return new SpannableString(context.getString(R.string.MessageRecord_left_group)); } else if (isGroupQuit()) { return new SpannableString(context.getString(R.string.ConversationItem_group_action_left, getIndividualRecipient().toShortString())); - } else if (isIncomingCall()) { - return new SpannableString(context.getString(R.string.MessageRecord_s_called_you, getIndividualRecipient().toShortString())); - } else if (isOutgoingCall()) { - return new SpannableString(context.getString(R.string.MessageRecord_you_called)); - } else if (isMissedCall()) { - return new SpannableString(context.getString(R.string.MessageRecord_missed_call)); - } else if (isJoined()) { - return new SpannableString(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().toShortString())); - } else if (isExpirationTimerUpdate()) { - 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())); - } - 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)); - } else if (isIdentityUpdate()) { - return new SpannableString(context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, getIndividualRecipient().toShortString())); - } else if (isIdentityVerified()) { - if (isOutgoing()) return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified, getIndividualRecipient().toShortString())); - else return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device, getIndividualRecipient().toShortString())); - } else if (isIdentityDefault()) { - if (isOutgoing()) return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, getIndividualRecipient().toShortString())); - else return new SpannableString(context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified_from_another_device, getIndividualRecipient().toShortString())); } return new SpannableString(getBody()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java index e2a286c854..eab5931c84 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java @@ -28,6 +28,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.session.libsession.messaging.threads.recipients.Recipient; +import org.session.libsession.messaging.utilities.UpdateMessageBuilder; +import org.session.libsession.messaging.utilities.UpdateMessageData; import org.session.libsession.utilities.ExpirationUtil; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.SmsDatabase; @@ -73,7 +75,7 @@ public class ThreadRecord extends DisplayRecord { @Override public SpannableString getDisplayBody(@NonNull Context context) { Recipient recipient = getRecipient(); - if (isGroupUpdate()) { + if (isGroupUpdate() || isGroupUpdateMessage()) { return emphasisAdded(context.getString(R.string.ThreadRecord_group_updated)); } else if (isGroupQuit()) { return emphasisAdded(context.getString(R.string.ThreadRecord_left_the_group)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt index dd642beb59..3844b9fbf6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt @@ -13,7 +13,7 @@ import network.loki.messenger.R import org.session.libsignal.utilities.logging.Log import java.io.IOException -//TODO Brice: that class should be deprecated +//TODO that class isn't used anymore class GroupDescription(context: Context, groupContext: SignalServiceProtos.GroupContext?) { private val context: Context private val groupContext: SignalServiceProtos.GroupContext? 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 8d10605fc8..dd23927bc3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -2,16 +2,12 @@ package org.thoughtcrime.securesms.service; import android.content.Context; -import com.google.protobuf.ByteString; import org.jetbrains.annotations.NotNull; import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate; -import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage; -import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage; +import org.session.libsession.messaging.messages.signal.OutgoingExpirationUpdateMessage; import org.session.libsession.messaging.threads.Address; -import org.session.libsession.messaging.threads.DistributionTypes; import org.session.libsession.messaging.threads.recipients.Recipient; -import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.TextSecurePreferences; @@ -27,7 +23,6 @@ import org.session.libsession.messaging.messages.signal.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.MmsException; import java.io.IOException; -import java.util.Collections; import java.util.Comparator; import java.util.TreeSet; import java.util.concurrent.Executor; @@ -97,7 +92,6 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM Long sentTimestamp = message.getSentTimestamp(); String groupId = message.getGroupPublicKey(); int duration = message.getDuration(); - String messageBody = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, duration, senderPublicKey, false); Optional groupInfo = Optional.absent(); Address address = Address.fromSerialized(senderPublicKey); @@ -118,7 +112,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM IncomingMediaMessage mediaMessage = new IncomingMediaMessage(address, sentTimestamp, -1, duration * 1000L, true, false, - Optional.of(messageBody), + Optional.absent(), groupInfo, Optional.absent(), Optional.absent(), @@ -141,36 +135,18 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM Long sentTimestamp = message.getSentTimestamp(); String groupId = message.getGroupPublicKey(); int duration = message.getDuration(); - String messageBody = UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, duration, null, true); Address address = Address.fromSerialized((message.getSyncTarget() != null && !message.getSyncTarget().isEmpty()) ? message.getSyncTarget() : message.getRecipient()); Recipient recipient = Recipient.from(context, address, false); try { + OutgoingExpirationUpdateMessage timerUpdateMessage = new OutgoingExpirationUpdateMessage(recipient, sentTimestamp, duration * 1000L, groupId); + database.insertSecureDecryptedMessageOutbox(timerUpdateMessage, -1, sentTimestamp); + if (groupId != null) { - // conversation is a closed group - OutgoingGroupMediaMessage infoMessage = new OutgoingGroupMediaMessage(recipient, messageBody, groupId, null, sentTimestamp, duration * 1000L, true, null, Collections.emptyList(), Collections.emptyList()); - database.insertSecureDecryptedMessageOutbox(infoMessage, -1, sentTimestamp); // we need the group ID as recipient for setExpireMessages below recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.doubleEncodeGroupID(groupId)), false); - } else { - // conversation is a 1-1 - OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipient, - messageBody, - Collections.emptyList(), - message.getSentTimestamp(), - -1, - duration * 1000L, - true, - DistributionTypes.DEFAULT, - null, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList()); - database.insertSecureDecryptedMessageOutbox(mediaMessage, -1, sentTimestamp); } - //set the timer to the conversation DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, duration); diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java index 901f151cdf..125267afb7 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/IncomingGroupMessage.java @@ -5,10 +5,12 @@ import static org.session.libsignal.service.internal.push.SignalServiceProtos.Gr public class IncomingGroupMessage extends IncomingTextMessage { private final String groupID; + private final boolean updateMessage; - public IncomingGroupMessage(IncomingTextMessage base, String groupID, String body) { + public IncomingGroupMessage(IncomingTextMessage base, String groupID, String body, boolean updateMessage) { super(base, body); this.groupID = groupID; + this.updateMessage = updateMessage; } @Override @@ -16,4 +18,6 @@ public class IncomingGroupMessage extends IncomingTextMessage { return true; } + public boolean isUpdateMessage() { return updateMessage; } + } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java index 087751e2fd..b45f33c78f 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingExpirationUpdateMessage.java @@ -1,6 +1,5 @@ package org.session.libsession.messaging.messages.signal; -import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate; import org.session.libsession.messaging.sending_receiving.attachments.Attachment; import org.session.libsession.messaging.threads.DistributionTypes; import org.session.libsession.messaging.threads.recipients.Recipient; @@ -8,18 +7,15 @@ import org.session.libsession.messaging.threads.recipients.Recipient; import java.util.Collections; import java.util.LinkedList; -// TODO this class could be deleted if its usage in MmsDatabase.getOutgoingMessage is replaced by something elsex public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage { - public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn) { - super(recipient, "", new LinkedList(), sentTimeMillis, - DistributionTypes.CONVERSATION, expiresIn, true, null, Collections.emptyList(), - Collections.emptyList()); - } + private final String groupId; - public static OutgoingExpirationUpdateMessage from(ExpirationTimerUpdate message, - Recipient recipient) { - return new OutgoingExpirationUpdateMessage(recipient, message.getSentTimestamp(), message.getDuration() * 1000); + public OutgoingExpirationUpdateMessage(Recipient recipient, long sentTimeMillis, long expiresIn, String groupId) { + super(recipient, "", new LinkedList(), sentTimeMillis, + DistributionTypes.CONVERSATION, expiresIn, null, Collections.emptyList(), + Collections.emptyList()); + this.groupId = groupId; } @Override @@ -27,4 +23,13 @@ public class OutgoingExpirationUpdateMessage extends OutgoingSecureMediaMessage return true; } + @Override + public boolean isGroup() { + return groupId != null; + } + + public String getGroupId() { + return groupId; + } + } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java index a726286be6..574c47cd19 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingGroupMediaMessage.java @@ -20,23 +20,7 @@ import java.util.List; public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { private final String groupID; - - public OutgoingGroupMediaMessage(@NonNull Recipient recipient, - @NonNull String body, - @Nullable String groupId, - @NonNull List avatar, - long sentTimeMillis, - long expiresIn, - @Nullable QuoteModel quote, - @NonNull List contacts, - @NonNull List previews) - throws IOException - { - super(recipient, body, avatar, sentTimeMillis, - DistributionTypes.CONVERSATION, expiresIn, false, quote, contacts, previews); - - this.groupID = groupId; - } + private final boolean isUpdateMessage; public OutgoingGroupMediaMessage(@NonNull Recipient recipient, @NonNull String body, @@ -44,7 +28,7 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { @Nullable final Attachment avatar, long sentTime, long expireIn, - boolean expirationUpdate, + boolean updateMessage, @Nullable QuoteModel quote, @NonNull List contacts, @NonNull List previews) @@ -52,9 +36,10 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { super(recipient, body, new LinkedList() {{if (avatar != null) add(avatar);}}, sentTime, - DistributionTypes.CONVERSATION, expireIn, expirationUpdate, quote, contacts, previews); + DistributionTypes.CONVERSATION, expireIn, quote, contacts, previews); this.groupID = groupId; + this.isUpdateMessage = updateMessage; } @Override @@ -65,4 +50,8 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage { public String getGroupId() { return groupID; } + + public boolean isUpdateMessage() { + return isUpdateMessage; + } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.java index 9a6618dc4a..d8d4dff3c5 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.java @@ -26,7 +26,6 @@ public class OutgoingMediaMessage { private final int distributionType; private final int subscriptionId; private final long expiresIn; - private final boolean expirationUpdate; private final QuoteModel outgoingQuote; private final List networkFailures = new LinkedList<>(); @@ -37,7 +36,6 @@ public class OutgoingMediaMessage { public OutgoingMediaMessage(Recipient recipient, String message, List attachments, long sentTimeMillis, int subscriptionId, long expiresIn, - boolean expirationUpdate, int distributionType, @Nullable QuoteModel outgoingQuote, @NonNull List contacts, @@ -52,7 +50,6 @@ public class OutgoingMediaMessage { this.attachments = attachments; this.subscriptionId = subscriptionId; this.expiresIn = expiresIn; - this.expirationUpdate = expirationUpdate; this.outgoingQuote = outgoingQuote; this.contacts.addAll(contacts); @@ -69,7 +66,6 @@ public class OutgoingMediaMessage { this.sentTimeMillis = that.sentTimeMillis; this.subscriptionId = that.subscriptionId; this.expiresIn = that.expiresIn; - this.expirationUpdate = that.expirationUpdate; this.outgoingQuote = that.outgoingQuote; this.identityKeyMismatches.addAll(that.identityKeyMismatches); @@ -89,7 +85,7 @@ public class OutgoingMediaMessage { previews = Collections.singletonList(linkPreview); } return new OutgoingMediaMessage(recipient, message.getText(), attachments, message.getSentTimestamp(), -1, - recipient.getExpireMessages() * 1000, false, DistributionTypes.DEFAULT, outgoingQuote, Collections.emptyList(), + recipient.getExpireMessages() * 1000, DistributionTypes.DEFAULT, outgoingQuote, Collections.emptyList(), previews, Collections.emptyList(), Collections.emptyList()); } @@ -113,7 +109,7 @@ public class OutgoingMediaMessage { return false; } - public boolean isExpirationUpdate() { return expirationUpdate; } + public boolean isExpirationUpdate() { return false; } public long getSentTimeMillis() { return sentTimeMillis; diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingSecureMediaMessage.java b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingSecureMediaMessage.java index c7822d8b90..8b5e7ddef0 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingSecureMediaMessage.java +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingSecureMediaMessage.java @@ -19,12 +19,11 @@ public class OutgoingSecureMediaMessage extends OutgoingMediaMessage { long sentTimeMillis, int distributionType, long expiresIn, - boolean expirationUpdate, @Nullable QuoteModel quote, @NonNull List contacts, @NonNull List previews) { - super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, expirationUpdate, distributionType, quote, contacts, previews, Collections.emptyList(), Collections.emptyList()); + super(recipient, body, attachments, sentTimeMillis, -1, expiresIn, distributionType, quote, contacts, previews, Collections.emptyList(), Collections.emptyList()); } public OutgoingSecureMediaMessage(OutgoingMediaMessage base) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index 2e2163f1ad..6d3dbb858d 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -1,17 +1,15 @@ package org.session.libsession.messaging.utilities import android.content.Context -import android.text.SpannableString import org.session.libsession.R import org.session.libsession.messaging.MessagingConfiguration -import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate import org.session.libsession.utilities.ExpirationUtil import org.session.libsignal.service.api.messages.SignalServiceGroup object UpdateMessageBuilder { - fun buildGroupUpdateMessage(context: Context, groupInfo: SignalServiceGroup, sender: String? = null, isOutgoing: Boolean = false): String { - val updateType = groupInfo.type + fun buildGroupUpdateMessage(context: Context, updateData: UpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String { + val updateType = updateData.type var message: String = "" if (!isOutgoing && sender == null) return message val senderName: String? = if (!isOutgoing) { @@ -28,13 +26,13 @@ object UpdateMessageBuilder { } SignalServiceGroup.Type.NAME_CHANGE -> { message = if (isOutgoing) { - context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, groupInfo.name.get()) + context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, updateData.groupName) } else { - context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, groupInfo.name.get()) + context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, updateData.groupName) } } SignalServiceGroup.Type.MEMBER_ADDED -> { - val members = groupInfo.members.get().joinToString(", ") { + val members = updateData.updatedMembers.joinToString(", ") { MessagingConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it } message = if (isOutgoing) { @@ -47,7 +45,7 @@ object UpdateMessageBuilder { val storage = MessagingConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey()!! // 1st case: you are part of the removed members - message = if (userPublicKey in groupInfo.members.get()) { + message = if (userPublicKey in updateData.updatedMembers) { if (isOutgoing) { context.getString(R.string.MessageRecord_left_group) } else { @@ -55,7 +53,7 @@ object UpdateMessageBuilder { } } else { // 2nd case: you are not part of the removed members - val members = groupInfo.members.get().joinToString(", ") { + val members = updateData.updatedMembers.joinToString(", ") { storage.getDisplayNameForRecipient(it) ?: it } if (isOutgoing) { @@ -94,14 +92,8 @@ object UpdateMessageBuilder { } } - //TODO one this is merged in + //TODO do this when the current update is merged fun buildDataExtractionMessage(): String { return "" } - - /*TODO retro compatibilite old update messages (MessageRecord) - ThreadRecord to display specific messages? (hard unless we can get the incoming / outgoing messages) - Clean code (comments, logs...) - Delete OutgoingExpirationUpdateMessage (check how its used in MmsDatabase l.520 to save messages and how to do the same when getting messages from db without breaking it) - */ } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt new file mode 100644 index 0000000000..bca6c7acf4 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt @@ -0,0 +1,48 @@ +package org.session.libsession.messaging.utilities + +import com.fasterxml.jackson.core.JsonParseException +import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.utilities.JsonUtil +import org.session.libsignal.utilities.logging.Log +import java.util.* + +// class used to save update messages details +class UpdateMessageData () { + + var type: SignalServiceGroup.Type = SignalServiceGroup.Type.UNKNOWN + var groupName: String? = null + var updatedMembers: Collection = Collections.emptyList() + + constructor(type: SignalServiceGroup.Type, groupName: String?, updatedMembers: Collection): this() { + this.type = type + this.groupName = groupName + this.updatedMembers = updatedMembers + } + + companion object { + val TAG = UpdateMessageData::class.simpleName + + fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection): UpdateMessageData { + return when(type) { + SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(type, name, Collections.emptyList()) + SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(type,null, members) + SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(type,null, members) + else -> UpdateMessageData(type,null, Collections.emptyList()) + } + } + + fun fromJSON(json: String): UpdateMessageData { + return try { + JsonUtil.fromJson(json, UpdateMessageData::class.java) + } catch (e: JsonParseException) { + Log.e(TAG, "${e.message}") + UpdateMessageData(SignalServiceGroup.Type.UNKNOWN, null, Collections.emptyList()) + } + } + } + + fun toJSON(): String { + return JsonUtil.toJson(this) + } + +} \ No newline at end of file From a346bb4ea528db020c7e9cd13893046c5fcdcd46 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 15 Apr 2021 15:44:42 +1000 Subject: [PATCH 20/36] clean --- .../libsession/messaging/utilities/UpdateMessageBuilder.kt | 2 +- .../libsession/messaging/utilities/UpdateMessageData.kt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index 6d3dbb858d..85be978379 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -96,4 +96,4 @@ object UpdateMessageBuilder { fun buildDataExtractionMessage(): String { return "" } -} \ No newline at end of file +} diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt index bca6c7acf4..a21e403b78 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt @@ -44,5 +44,4 @@ class UpdateMessageData () { fun toJSON(): String { return JsonUtil.toJson(this) } - -} \ No newline at end of file +} From 2a1dfff8c46e5da8eb01f803fc2a674a119c7ddb Mon Sep 17 00:00:00 2001 From: Brice-W Date: Thu, 15 Apr 2021 16:33:40 +1000 Subject: [PATCH 21/36] translations added --- app/src/main/res/values-fr/strings.xml | 40 ++++++++++++++++++++++++++ app/src/main/res/values/strings.xml | 4 +-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d562e0c62a..728266183e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -418,6 +418,15 @@ Vous avez reçu un message chiffré avec une ancienne version de Signal qui n’est plus prise en charge. Veuillez demander à l’expéditeur de mettre Session à jour vers la version la plus récente et de renvoyer son message. Vous avez quitté le groupe Vous avez mis le groupe à jour. + Vous avez créée un nouveau groupe. + %1$s vous a ajouté au groupe. + Vous avez renommé le groupe en \'%1$s\' + %1$s a renommé le groupe en \'%2$s\' + Vous avez ajouté %1$s au groupe. + %1$s a ajouté %2$s au groupe. + Vous avez supprimé %1$s du groupe. + %1$s a supprimé %2$s du groupe. + Vous avez été supprimé du groupe. Vous avez appelé Contact appelé Appel manqué @@ -1483,4 +1492,35 @@ Vous avez reçu un message d’échange de clés pour une version de protocole i Groupes privés Groupes publics + + + Appliquer + Terminé + + Modifier le groupe + Saisissez un nouveau nom de groupe + Membres + Ajouter des membres + Le nom du groupe ne peut pas être vide + Veuillez saisir un nom plus court + Les groupes doivent avoir au moins un membre + Un des membres du group a un Session ID invalide + Êtes vous sûr de vouloir suprimer ce membre du groupe? + Membre supprimé du groupe + + Supprimer le membre du groupe + + Contacts + + Thème + Jour + Nuit + Système + + Copier le Session ID + + Pièce jointe + Message vocal + Détails + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ac5121c1d7..4ed3697e63 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -495,8 +495,8 @@ You updated the group. You created a new group. %1$s added you to the group. - You renamed the group to %1$s - %1$s renamed the group to: %2$s + You renamed the group to \'%1$s\' + %1$s renamed the group to \'%2$s\' You added %1$s to the group. %1$s added %2$s to the group. You removed %1$s from the group. From 2b7cf7c1b446d28d70858e98769395c740ee14a5 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 16 Apr 2021 15:54:39 +1000 Subject: [PATCH 22/36] added Kind sealed class in UpdateMessageData + minor fixes --- .../loki/utilities/GroupDescription.kt | 104 ------------------ app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../utilities/UpdateMessageBuilder.kt | 7 +- .../messaging/utilities/UpdateMessageData.kt | 35 +++--- libsession/src/main/res/values/strings.xml | 1 + 6 files changed, 29 insertions(+), 120 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt deleted file mode 100644 index 3844b9fbf6..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/GroupDescription.kt +++ /dev/null @@ -1,104 +0,0 @@ -package org.thoughtcrime.securesms.loki.utilities - -import android.content.Context -import org.session.libsession.messaging.threads.Address -import org.session.libsession.messaging.threads.recipients.Recipient -import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.Base64 -import org.session.libsignal.service.internal.push.SignalServiceProtos -import java.util.* - -import network.loki.messenger.R -import org.session.libsignal.utilities.logging.Log -import java.io.IOException - -//TODO that class isn't used anymore -class GroupDescription(context: Context, groupContext: SignalServiceProtos.GroupContext?) { - private val context: Context - private val groupContext: SignalServiceProtos.GroupContext? - private val newMembers: MutableList - private val removedMembers: MutableList - private var wasCurrentUserRemoved: Boolean = false - private fun toRecipient(hexEncodedPublicKey: String): Recipient { - val address = Address.fromSerialized(hexEncodedPublicKey) - return Recipient.from(context, address, false) - } - - fun toString(sender: Recipient): String { - if (wasCurrentUserRemoved) { - return context.getString(R.string.GroupUtil_you_were_removed_from_group) - } - val description = StringBuilder() - description.append(context.getString(R.string.MessageRecord_s_updated_group, sender.toShortString())) - if (groupContext == null) { - return description.toString() - } - val title = groupContext.name - if (!newMembers.isEmpty()) { - description.append("\n") - description.append(context.resources.getQuantityString(R.plurals.GroupUtil_joined_the_group, - newMembers.size, toString(newMembers))) - } - if (!removedMembers.isEmpty()) { - description.append("\n") - description.append(context.resources.getQuantityString(R.plurals.GroupUtil_removed_from_the_group, - removedMembers.size, toString(removedMembers))) - } - if (title != null && !title.trim { it <= ' ' }.isEmpty()) { - val separator = if (!newMembers.isEmpty() || !removedMembers.isEmpty()) " " else "\n" - description.append(separator) - description.append(context.getString(R.string.GroupUtil_group_name_is_now, title)) - } - return description.toString() - } - - fun addListener(listener: RecipientModifiedListener?) { - if (!newMembers.isEmpty()) { - for (member in newMembers) { - member.addListener(listener) - } - } - } - - private fun toString(recipients: List): String { - var result = "" - for (i in recipients.indices) { - result += recipients[i].toShortString() - if (i != recipients.size - 1) result += ", " - } - return result - } - - init { - this.context = context.applicationContext - this.groupContext = groupContext - newMembers = LinkedList() - removedMembers = LinkedList() - if (groupContext != null) { - val newMembers = groupContext.newMembersList - for (member in newMembers) { - this.newMembers.add(toRecipient(member)) - } - val removedMembers = groupContext.removedMembersList - for (member in removedMembers) { - this.removedMembers.add(toRecipient(member)) - } - wasCurrentUserRemoved = removedMembers.contains(TextSecurePreferences.getLocalNumber(context)) - } - } - - companion object { - fun getDescription(context: Context, encodedGroup: String?): GroupDescription { - return if (encodedGroup == null) { - GroupDescription(context, null) - } else try { - val groupContext = SignalServiceProtos.GroupContext.parseFrom(Base64.decode(encodedGroup)) - GroupDescription(context, groupContext) - } catch (e: IOException) { - Log.w("Loki", e) - GroupDescription(context, null) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 728266183e..f156dad77b 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -427,6 +427,7 @@ Vous avez supprimé %1$s du groupe. %1$s a supprimé %2$s du groupe. Vous avez été supprimé du groupe. + Vous Vous avez appelé Contact appelé Appel manqué diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4ed3697e63..c7279e90ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -502,6 +502,7 @@ You removed %1$s from the group. %1$s removed %2$s from the group. You were removed from the group. + You You called Contact called Missed call diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index 85be978379..d6f446e364 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -8,13 +8,14 @@ import org.session.libsignal.service.api.messages.SignalServiceGroup object UpdateMessageBuilder { - fun buildGroupUpdateMessage(context: Context, updateData: UpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String { - val updateType = updateData.type + fun buildGroupUpdateMessage(context: Context, updateMessageData: UpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String { var message: String = "" + val updateData = updateMessageData.kind as? UpdateMessageData.Kind.GroupUpdate ?: return message + val updateType = updateData.type if (!isOutgoing && sender == null) return message val senderName: String? = if (!isOutgoing) { MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender - } else { sender } + } else { context.getString(R.string.MessageRecord_you) } when (updateType) { SignalServiceGroup.Type.CREATION -> { diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt index a21e403b78..5ca4bbbf9c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt @@ -1,5 +1,7 @@ package org.session.libsession.messaging.utilities +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.core.JsonParseException import org.session.libsignal.service.api.messages.SignalServiceGroup import org.session.libsignal.utilities.JsonUtil @@ -9,14 +11,21 @@ import java.util.* // class used to save update messages details class UpdateMessageData () { - var type: SignalServiceGroup.Type = SignalServiceGroup.Type.UNKNOWN - var groupName: String? = null - var updatedMembers: Collection = Collections.emptyList() + var kind: Kind? = null - constructor(type: SignalServiceGroup.Type, groupName: String?, updatedMembers: Collection): this() { - this.type = type - this.groupName = groupName - this.updatedMembers = updatedMembers + //the annotations below are required for serialization. Any new Kind class MUST be declared as JsonSubTypes as well + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) + @JsonSubTypes( + JsonSubTypes.Type(Kind.GroupUpdate::class, name = "GroupUpdate") + ) + sealed class Kind { + class GroupUpdate( var type: SignalServiceGroup.Type, var groupName: String?, var updatedMembers: Collection): Kind() { + constructor(): this(SignalServiceGroup.Type.UNKNOWN, null, Collections.emptyList()) //default constructor required for json serialization + } + } + + constructor(kind: Kind): this() { + this.kind = kind } companion object { @@ -24,19 +33,19 @@ class UpdateMessageData () { fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection): UpdateMessageData { return when(type) { - SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(type, name, Collections.emptyList()) - SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(type,null, members) - SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(type,null, members) - else -> UpdateMessageData(type,null, Collections.emptyList()) + SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(Kind.GroupUpdate(type, name, Collections.emptyList())) + SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(Kind.GroupUpdate(type,null, members)) + SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(Kind.GroupUpdate(type,null, members)) + else -> UpdateMessageData(Kind.GroupUpdate(type,null, Collections.emptyList())) } } fun fromJSON(json: String): UpdateMessageData { - return try { + return try { JsonUtil.fromJson(json, UpdateMessageData::class.java) } catch (e: JsonParseException) { Log.e(TAG, "${e.message}") - UpdateMessageData(SignalServiceGroup.Type.UNKNOWN, null, Collections.emptyList()) + UpdateMessageData(Kind.GroupUpdate(SignalServiceGroup.Type.UNKNOWN, null, Collections.emptyList())) } } } diff --git a/libsession/src/main/res/values/strings.xml b/libsession/src/main/res/values/strings.xml index 90cbb92869..116b106017 100644 --- a/libsession/src/main/res/values/strings.xml +++ b/libsession/src/main/res/values/strings.xml @@ -496,6 +496,7 @@ You removed %1$s from the group. %1$s removed %2$s from the group. You were removed from the group. + You You called Contact called Missed call From 3c210ca437e2e4a15ba70f33f035eb6e544e89ab Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 19 Apr 2021 10:46:19 +1000 Subject: [PATCH 23/36] optimization on contacts selection --- .../securesms/loki/activities/SelectContactsAdapter.kt | 8 ++++---- .../org/thoughtcrime/securesms/loki/views/UserView.kt | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt index 135762f661..c81c0e38d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt @@ -26,7 +26,7 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { val member = members[position] - viewHolder.view.setOnClickListener { onMemberClick(member) } + viewHolder.view.setOnClickListener { onMemberClick(viewHolder, member) } val isSelected = selectedMembers.contains(member) viewHolder.view.bind(Recipient.from( context, @@ -36,13 +36,13 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli isSelected) } - private fun onMemberClick(member: String) { + private fun onMemberClick(viewHolder: ViewHolder, member: String) { if (selectedMembers.contains(member)) { selectedMembers.remove(member) + viewHolder.view.toggleCheckbox() } else { selectedMembers.add(member) + viewHolder.view.toggleCheckbox(true) } - val index = members.indexOf(member) - notifyItemChanged(index) } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt index 4c6098a710..ae9f001f5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt @@ -82,6 +82,11 @@ class UserView : LinearLayout { } } + fun toggleCheckbox(isSelected: Boolean = false) { + actionIndicatorImageView.visibility = View.VISIBLE + actionIndicatorImageView.setImageResource(if (isSelected) R.drawable.ic_circle_check else R.drawable.ic_circle) + } + fun unbind() { } From cba4e65b9e15022c2b164883e54962b946def490 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 19 Apr 2021 13:35:09 +1000 Subject: [PATCH 24/36] update with payload logic --- .../loki/activities/SelectContactsAdapter.kt | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt index c81c0e38d3..4fb31d9fdb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt @@ -26,7 +26,7 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { val member = members[position] - viewHolder.view.setOnClickListener { onMemberClick(viewHolder, member) } + viewHolder.view.setOnClickListener { onMemberClick(member) } val isSelected = selectedMembers.contains(member) viewHolder.view.bind(Recipient.from( context, @@ -36,13 +36,39 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli isSelected) } - private fun onMemberClick(viewHolder: ViewHolder, member: String) { - if (selectedMembers.contains(member)) { - selectedMembers.remove(member) - viewHolder.view.toggleCheckbox() + override fun onBindViewHolder(viewHolder: ViewHolder, + position: Int, + payloads: MutableList) { + if (payloads.isNotEmpty()) { + // Because these updates can be batched, + // there can be multiple payloads for a single bind + when (payloads[0]) { + Payload.MEMBER_CLICKED -> { + val member = members[position] + val isSelected = selectedMembers.contains(member) + viewHolder.view.toggleCheckbox(isSelected) + } + } } else { - selectedMembers.add(member) - viewHolder.view.toggleCheckbox(true) + // When payload list is empty, + // or we don't have logic to handle a given type, + // default to full bind: + this.onBindViewHolder(viewHolder, position) } } + + private fun onMemberClick(member: String) { + if (selectedMembers.contains(member)) { + selectedMembers.remove(member) + } else { + selectedMembers.add(member) + } + val index = members.indexOf(member) + notifyItemChanged(index, Payload.MEMBER_CLICKED) + } + + // define below the different events used to notify the adapter + enum class Payload { + MEMBER_CLICKED + } } \ No newline at end of file From b92f2e79048b10f72d7a14895507fb554e77263f Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 19 Apr 2021 13:36:42 +1000 Subject: [PATCH 25/36] clean --- .../main/java/org/thoughtcrime/securesms/loki/views/UserView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt index ae9f001f5c..19642a6c87 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/UserView.kt @@ -91,4 +91,4 @@ class UserView : LinearLayout { } // endregion -} \ No newline at end of file +} From be2692fec672cbf5cb3fe00d81752879b2d10b04 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Mon, 19 Apr 2021 13:37:34 +1000 Subject: [PATCH 26/36] clean --- .../securesms/loki/activities/SelectContactsAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt index 4fb31d9fdb..e8bbede270 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsAdapter.kt @@ -71,4 +71,4 @@ class SelectContactsAdapter(private val context: Context, private val glide: Gli enum class Payload { MEMBER_CLICKED } -} \ No newline at end of file +} From 10554011f8b1873afa55fd87fecf41198cb9d2f1 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 20 Apr 2021 09:39:51 +1000 Subject: [PATCH 27/36] avoid displaying self in the contacts list --- .../securesms/loki/activities/CreateClosedGroupActivity.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt index 2ad317bdba..bebbc58856 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt @@ -32,6 +32,10 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM set(newValue) { field = newValue; invalidateOptionsMenu() } private var members = listOf() set(value) { field = value; selectContactsAdapter.members = value } + private val publicKey: String + get() { + return TextSecurePreferences.getLocalNumber(this)!! + } private val selectContactsAdapter by lazy { SelectContactsAdapter(this, GlideApp.with(this)) @@ -72,7 +76,7 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM } private fun update(members: List) { - this.members = members + this.members = members.minus(publicKey) mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE invalidateOptionsMenu() From f9de34f9479ad9752fd30f6933d510a83aebdd87 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 20 Apr 2021 09:45:29 +1000 Subject: [PATCH 28/36] comment --- .../securesms/loki/activities/CreateClosedGroupActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt index bebbc58856..f3a7f94bd4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreateClosedGroupActivity.kt @@ -76,6 +76,7 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderM } private fun update(members: List) { + //if there is a Note to self conversation, it loads self in the list, so we need to remove it here this.members = members.minus(publicKey) mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE From 3bfef2d0cdf1a92eccfd9ebc6eae66f90f45a119 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 20 Apr 2021 16:55:46 +1000 Subject: [PATCH 29/36] e UpdateMessageBuilder to generate data extraction message --- .../securesms/database/model/MessageRecord.java | 4 ++++ app/src/main/res/values-fr/strings.xml | 2 ++ .../messaging/utilities/UpdateMessageBuilder.kt | 14 ++++++++++---- libsession/src/main/res/values/strings.xml | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) 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 af293d3833..954cedb150 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 @@ -27,6 +27,7 @@ import network.loki.messenger.R; import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.messaging.utilities.UpdateMessageData; +import org.session.libsignal.service.internal.push.SignalServiceProtos; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.SmsDatabase; import org.session.libsession.database.documents.IdentityKeyMismatch; @@ -97,6 +98,9 @@ public abstract class MessageRecord extends DisplayRecord { } else if (isExpirationTimerUpdate()) { int seconds = (int) (getExpiresIn() / 1000); return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing())); + } else if (isDataExtraction()) { + if (isScreenshotExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT, getIndividualRecipient().getAddress().serialize()))); + else if (isMediaSavedExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize()))); } // TODO below lines are left here for compatibility with older group update messages, it can be deleted later on else if (isGroupUpdate() && isOutgoing()) { diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f156dad77b..5fbc37a757 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -440,6 +440,8 @@ %1$s a désactivé les messages éphémères. Vous avez défini l’expiration des messages éphémères à %1$s. %1$s a défini l’expiration des messages éphémères à %2$s. + %1$s a pris une capture d’écran. + %1$s a sauvegardé un media. Votre numéro de sécurité avec %s a changé. Vous avez marqué votre numéro de sécurité avec %s comme vérifié Vous avez marqué votre numéro de sécurité avec %s comme vérifié à partir d’un autre appareil diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index d6f446e364..7404d2e3ca 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -5,6 +5,7 @@ import org.session.libsession.R import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.utilities.ExpirationUtil import org.session.libsignal.service.api.messages.SignalServiceGroup +import org.session.libsignal.service.internal.push.SignalServiceProtos object UpdateMessageBuilder { @@ -82,7 +83,7 @@ object UpdateMessageBuilder { if (!isOutgoing && sender == null) return "" val senderName: String? = if (!isOutgoing) { MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender - } else { sender } + } else { context.getString(R.string.MessageRecord_you) } return if (duration <= 0) { if (isOutgoing) context.getString(R.string.MessageRecord_you_disabled_disappearing_messages) else context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, senderName) @@ -93,8 +94,13 @@ object UpdateMessageBuilder { } } - //TODO do this when the current update is merged - fun buildDataExtractionMessage(): String { - return "" + fun buildDataExtractionMessage(context: Context, kind: SignalServiceProtos.DataExtractionNotification.Type, sender: String? = null): String { + val senderName = MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender + return when (kind) { + SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT -> + context.getString(R.string.MessageRecord_s_took_a_screenshot, senderName) + SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED -> + context.getString(R.string.MessageRecord_media_saved_by_s, senderName) + } } } diff --git a/libsession/src/main/res/values/strings.xml b/libsession/src/main/res/values/strings.xml index 116b106017..ee0510385c 100644 --- a/libsession/src/main/res/values/strings.xml +++ b/libsession/src/main/res/values/strings.xml @@ -509,6 +509,8 @@ %1$s disabled disappearing messages. You set the disappearing message timer to %1$s %1$s set the disappearing message timer to %2$s + %1$s took a screenshot. + 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 From 7aa1f30c986574f6af7b6ada06ad5e20b7be34fe Mon Sep 17 00:00:00 2001 From: Brice-W Date: Tue, 20 Apr 2021 17:07:21 +1000 Subject: [PATCH 30/36] fix --- .../securesms/conversation/ConversationUpdateItem.java | 4 ++-- .../securesms/database/model/MessageRecord.java | 6 +++--- .../messaging/messages/signal/IncomingMediaMessage.java | 2 +- .../messaging/sending_receiving/MessageReceiverHandler.kt | 2 +- .../DataExtractionNotificationInfoMessage.kt | 2 +- .../messaging/utilities/UpdateMessageBuilder.kt | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 7218006755..91d1c4244b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -106,7 +106,7 @@ public class ConversationUpdateItem extends LinearLayout else if (messageRecord.isJoined()) setJoinedRecord(messageRecord); else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord); else if (messageRecord.isScreenshotExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT); - else if (messageRecord.isMediaSavedExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.MEDIASAVED); + else if (messageRecord.isMediaSavedExtraction()) setDataExtractionRecord(messageRecord, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED); else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord); else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord); else if (messageRecord.isIdentityVerified() || @@ -154,7 +154,7 @@ public class ConversationUpdateItem extends LinearLayout @ColorInt int color = GeneralUtilitiesKt.getColorWithID(getResources(), R.color.text, getContext().getTheme()); if (kind == DataExtractionNotificationInfoMessage.Kind.SCREENSHOT) { icon.setImageResource(R.drawable.quick_camera_dark); - } else if (kind == DataExtractionNotificationInfoMessage.Kind.MEDIASAVED) { + } else if (kind == DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED) { icon.setImageResource(R.drawable.ic_file_download_white_36dp); } icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); 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 954cedb150..9ecb9bac2e 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 @@ -25,9 +25,9 @@ import android.text.style.StyleSpan; import network.loki.messenger.R; +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage; import org.session.libsession.messaging.utilities.UpdateMessageBuilder; import org.session.libsession.messaging.utilities.UpdateMessageData; -import org.session.libsignal.service.internal.push.SignalServiceProtos; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.SmsDatabase; import org.session.libsession.database.documents.IdentityKeyMismatch; @@ -99,8 +99,8 @@ public abstract class MessageRecord extends DisplayRecord { int seconds = (int) (getExpiresIn() / 1000); return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), isOutgoing())); } else if (isDataExtraction()) { - if (isScreenshotExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT, getIndividualRecipient().getAddress().serialize()))); - else if (isMediaSavedExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize()))); + if (isScreenshotExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize()))); + else if (isMediaSavedExtraction()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize()))); } // TODO below lines are left here for compatibility with older group update messages, it can be deleted later on else if (isGroupUpdate() && isOutgoing()) { 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 1d359a6c64..f235e84e92 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 @@ -131,7 +131,7 @@ public class IncomingMediaMessage { public boolean isMediaSavedDataExtraction() { if (dataExtractionNotification == null) return false; else { - return dataExtractionNotification.getKind() == DataExtractionNotificationInfoMessage.Kind.MEDIASAVED; + return dataExtractionNotification.getKind() == DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED; } } 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 e5d0c8e278..fc1b28e1cc 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 @@ -103,7 +103,7 @@ private fun MessageReceiver.handleDataExtractionNotification(message: DataExtrac 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) + is DataExtractionNotification.Kind.MediaSaved -> DataExtractionNotificationInfoMessage(DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED) else -> return } storage.insertDataExtractionNotificationMessage(senderPublicKey, notification, 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 index 108b51c7aa..aca360eca6 100644 --- 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 @@ -4,7 +4,7 @@ class DataExtractionNotificationInfoMessage { enum class Kind { SCREENSHOT, - MEDIASAVED + MEDIA_SAVED } var kind: Kind? = null diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index 7404d2e3ca..b2ad3ca2b3 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -3,9 +3,9 @@ package org.session.libsession.messaging.utilities import android.content.Context import org.session.libsession.R import org.session.libsession.messaging.MessagingConfiguration +import org.session.libsession.messaging.sending_receiving.dataextraction.DataExtractionNotificationInfoMessage import org.session.libsession.utilities.ExpirationUtil import org.session.libsignal.service.api.messages.SignalServiceGroup -import org.session.libsignal.service.internal.push.SignalServiceProtos object UpdateMessageBuilder { @@ -94,12 +94,12 @@ object UpdateMessageBuilder { } } - fun buildDataExtractionMessage(context: Context, kind: SignalServiceProtos.DataExtractionNotification.Type, sender: String? = null): String { + fun buildDataExtractionMessage(context: Context, kind: DataExtractionNotificationInfoMessage.Kind, sender: String? = null): String { val senderName = MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender return when (kind) { - SignalServiceProtos.DataExtractionNotification.Type.SCREENSHOT -> + DataExtractionNotificationInfoMessage.Kind.SCREENSHOT -> context.getString(R.string.MessageRecord_s_took_a_screenshot, senderName) - SignalServiceProtos.DataExtractionNotification.Type.MEDIA_SAVED -> + DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED -> context.getString(R.string.MessageRecord_media_saved_by_s, senderName) } } From 99fa7eb767def182596a5701cbcf144b069ee13b Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 21 Apr 2021 12:00:25 +1000 Subject: [PATCH 31/36] added specific UpdateMessageData.King types --- .../securesms/database/Storage.kt | 4 +-- .../utilities/UpdateMessageBuilder.kt | 26 ++++++-------- .../messaging/utilities/UpdateMessageData.kt | 36 +++++++++++++------ 3 files changed, 38 insertions(+), 28 deletions(-) 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 dc2452b51d..7b2a0e293b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -405,7 +405,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection, admins: Collection, sentTimestamp: Long) { val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList()) val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true) - val updateData = UpdateMessageData.buildGroupUpdate(type, name, members).toJSON() + val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() val infoMessage = IncomingGroupMessage(m, groupID, updateData, true) val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) @@ -415,7 +415,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val userPublicKey = getUserPublicKey() val recipient = Recipient.from(context, Address.fromSerialized(groupID), false) - val updateData = UpdateMessageData.buildGroupUpdate(type, name, members).toJSON() + val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: "" val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, 0, true, null, listOf(), listOf()) val mmsDB = DatabaseFactory.getMmsDatabase(context) val mmsSmsDB = DatabaseFactory.getMmsSmsDatabase(context) diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index d6f446e364..3999e13e6b 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -9,30 +9,29 @@ import org.session.libsignal.service.api.messages.SignalServiceGroup object UpdateMessageBuilder { fun buildGroupUpdateMessage(context: Context, updateMessageData: UpdateMessageData, sender: String? = null, isOutgoing: Boolean = false): String { - var message: String = "" - val updateData = updateMessageData.kind as? UpdateMessageData.Kind.GroupUpdate ?: return message - val updateType = updateData.type + var message = "" + val updateData = updateMessageData.kind ?: return message if (!isOutgoing && sender == null) return message - val senderName: String? = if (!isOutgoing) { + val senderName: String = if (!isOutgoing) { MessagingConfiguration.shared.storage.getDisplayNameForRecipient(sender!!) ?: sender } else { context.getString(R.string.MessageRecord_you) } - when (updateType) { - SignalServiceGroup.Type.CREATION -> { + when (updateData) { + is UpdateMessageData.Kind.GroupCreation -> { message = if (isOutgoing) { context.getString(R.string.MessageRecord_you_created_a_new_group) } else { context.getString(R.string.MessageRecord_s_added_you_to_the_group, senderName) } } - SignalServiceGroup.Type.NAME_CHANGE -> { + is UpdateMessageData.Kind.GroupNameChange -> { message = if (isOutgoing) { - context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, updateData.groupName) + context.getString(R.string.MessageRecord_you_renamed_the_group_to_s, updateData.name) } else { - context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, updateData.groupName) + context.getString(R.string.MessageRecord_s_renamed_the_group_to_s, senderName, updateData.name) } } - SignalServiceGroup.Type.MEMBER_ADDED -> { + is UpdateMessageData.Kind.GroupMemberAdded -> { val members = updateData.updatedMembers.joinToString(", ") { MessagingConfiguration.shared.storage.getDisplayNameForRecipient(it) ?: it } @@ -42,7 +41,7 @@ object UpdateMessageBuilder { context.getString(R.string.MessageRecord_s_added_s_to_the_group, senderName, members) } } - SignalServiceGroup.Type.MEMBER_REMOVED -> { + is UpdateMessageData.Kind.GroupMemberRemoved -> { val storage = MessagingConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey()!! // 1st case: you are part of the removed members @@ -64,16 +63,13 @@ object UpdateMessageBuilder { } } } - SignalServiceGroup.Type.QUIT -> { + is UpdateMessageData.Kind.GroupMemberLeft -> { message = if (isOutgoing) { context.getString(R.string.MessageRecord_left_group) } else { context.getString(R.string.ConversationItem_group_action_left, senderName) } } - else -> { - message = context.getString(R.string.MessageRecord_s_updated_group, senderName) - } } return message } diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt index 5ca4bbbf9c..4285bab78e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageData.kt @@ -16,12 +16,24 @@ class UpdateMessageData () { //the annotations below are required for serialization. Any new Kind class MUST be declared as JsonSubTypes as well @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) @JsonSubTypes( - JsonSubTypes.Type(Kind.GroupUpdate::class, name = "GroupUpdate") + JsonSubTypes.Type(Kind.GroupCreation::class, name = "GroupCreation"), + JsonSubTypes.Type(Kind.GroupNameChange::class, name = "GroupNameChange"), + JsonSubTypes.Type(Kind.GroupMemberAdded::class, name = "GroupMemberAdded"), + JsonSubTypes.Type(Kind.GroupMemberRemoved::class, name = "GroupMemberRemoved"), + JsonSubTypes.Type(Kind.GroupMemberLeft::class, name = "GroupMemberLeft") ) - sealed class Kind { - class GroupUpdate( var type: SignalServiceGroup.Type, var groupName: String?, var updatedMembers: Collection): Kind() { - constructor(): this(SignalServiceGroup.Type.UNKNOWN, null, Collections.emptyList()) //default constructor required for json serialization + sealed class Kind() { + class GroupCreation(): Kind() + class GroupNameChange(val name: String): Kind() { + constructor(): this("") //default constructor required for json serialization } + class GroupMemberAdded(val updatedMembers: Collection): Kind() { + constructor(): this(Collections.emptyList()) + } + class GroupMemberRemoved(val updatedMembers: Collection): Kind() { + constructor(): this(Collections.emptyList()) + } + class GroupMemberLeft(): Kind() } constructor(kind: Kind): this() { @@ -31,21 +43,23 @@ class UpdateMessageData () { companion object { val TAG = UpdateMessageData::class.simpleName - fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection): UpdateMessageData { + fun buildGroupUpdate(type: SignalServiceGroup.Type, name: String, members: Collection): UpdateMessageData? { return when(type) { - SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(Kind.GroupUpdate(type, name, Collections.emptyList())) - SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(Kind.GroupUpdate(type,null, members)) - SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(Kind.GroupUpdate(type,null, members)) - else -> UpdateMessageData(Kind.GroupUpdate(type,null, Collections.emptyList())) + SignalServiceGroup.Type.CREATION -> UpdateMessageData(Kind.GroupCreation()) + SignalServiceGroup.Type.NAME_CHANGE -> UpdateMessageData(Kind.GroupNameChange(name)) + SignalServiceGroup.Type.MEMBER_ADDED -> UpdateMessageData(Kind.GroupMemberAdded(members)) + SignalServiceGroup.Type.MEMBER_REMOVED -> UpdateMessageData(Kind.GroupMemberRemoved(members)) + SignalServiceGroup.Type.QUIT -> UpdateMessageData(Kind.GroupMemberLeft()) + else -> null } } - fun fromJSON(json: String): UpdateMessageData { + fun fromJSON(json: String): UpdateMessageData? { return try { JsonUtil.fromJson(json, UpdateMessageData::class.java) } catch (e: JsonParseException) { Log.e(TAG, "${e.message}") - UpdateMessageData(Kind.GroupUpdate(SignalServiceGroup.Type.UNKNOWN, null, Collections.emptyList())) + null } } } From d1e276bf0804a27fff918a9ac9edaabf6f6e81e5 Mon Sep 17 00:00:00 2001 From: Brice-W Date: Wed, 21 Apr 2021 13:43:29 +1000 Subject: [PATCH 32/36] disable data extraction notification sending --- .../java/org/thoughtcrime/securesms/MediaOverviewActivity.java | 3 ++- .../java/org/thoughtcrime/securesms/MediaPreviewActivity.java | 3 ++- .../securesms/conversation/ConversationFragment.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index fe6f249b0b..66482c9c57 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -356,7 +356,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { // Sending a Data extraction notification (for incoming attachments only) boolean containsIncoming = mediaRecords.parallelStream().anyMatch(m -> !m.isOutgoing()); if (containsIncoming) { - sendMediaSavedNotificationIfNeeded(); + //TODO uncomment line below when Data extraction will be activated + //sendMediaSavedNotificationIfNeeded(); } } }.execute(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 876a33d09a..6edfca5e9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -354,7 +354,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); // Sending a Data extraction notification (for incoming attachments only) if(!mediaItem.outgoing) { - sendMediaSavedNotificationIfNeeded(); + //TODO uncomment line below when Data extraction will be activated + //sendMediaSavedNotificationIfNeeded(); } }) .execute(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 32668cb247..2aedad1119 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -748,7 +748,8 @@ public class ConversationFragment extends Fragment saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, attachments.toArray(new SaveAttachmentTask.Attachment[0])); // Sending a Data extraction notification (for incoming attachments only) if(!message.isOutgoing()) { - sendMediaSavedNotificationIfNeeded(); + //TODO uncomment line below when Data extraction will be activated + //sendMediaSavedNotificationIfNeeded(); } return; } From 572f262b6df21c0ce9872e2c980d855b7306cebc Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 26 Apr 2021 15:18:12 +1000 Subject: [PATCH 33/36] fix: don't update the profiles if the values are empty --- .../loki/protocol/MultiDeviceProtocol.kt | 89 ------------------- .../MessageReceiverHandler.kt | 26 +++--- 2 files changed, 13 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt index 41b3de72f8..b58f608502 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt @@ -1,25 +1,12 @@ package org.thoughtcrime.securesms.loki.protocol import android.content.Context -import com.google.protobuf.ByteString -import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.control.ConfigurationMessage import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.threads.Address -import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.preferences.ProfileKeyUtil -import org.session.libsignal.service.internal.push.SignalServiceProtos -import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage -import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded -import org.session.libsignal.utilities.Base64 -import org.session.libsignal.utilities.Hex -import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob import org.thoughtcrime.securesms.loki.utilities.ContactUtilities -import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities object MultiDeviceProtocol { @@ -51,80 +38,4 @@ object MultiDeviceProtocol { TextSecurePreferences.setLastConfigurationSyncTime(context, System.currentTimeMillis()) } - // TODO: remove this after we migrate to new message receiving pipeline - @JvmStatic - fun handleConfigurationMessage(context: Context, content: SignalServiceProtos.Content, senderPublicKey: String, timestamp: Long) { - synchronized(this) { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return - if (TextSecurePreferences.getConfigurationMessageSynced(context) && !TextSecurePreferences.shouldUpdateProfile(context, timestamp)) return - if (senderPublicKey != userPublicKey) return - TextSecurePreferences.setConfigurationMessageSynced(context, true) - TextSecurePreferences.setLastProfileUpdateTime(context, timestamp) - - val configurationMessage = ConfigurationMessage.fromProto(content) ?: return - - val storage = MessagingConfiguration.shared.storage - val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys() - - val threadDatabase = DatabaseFactory.getThreadDatabase(context) - val recipientDatabase = DatabaseFactory.getRecipientDatabase(context) - - val ourRecipient = Recipient.from(context, Address.fromSerialized(userPublicKey),false) - - for (closedGroup in configurationMessage.closedGroups) { - if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue - - val closedGroupUpdate = DataMessage.ClosedGroupControlMessage.newBuilder() - closedGroupUpdate.type = DataMessage.ClosedGroupControlMessage.Type.NEW - closedGroupUpdate.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(closedGroup.publicKey)) - closedGroupUpdate.name = closedGroup.name - val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder() - encryptionKeyPair.publicKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded()) - encryptionKeyPair.privateKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.privateKey.serialize()) - closedGroupUpdate.encryptionKeyPair = encryptionKeyPair.build() - closedGroupUpdate.addAllMembers(closedGroup.members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) }) - closedGroupUpdate.addAllAdmins(closedGroup.admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) }) - - ClosedGroupsProtocolV2.handleNewClosedGroup(context, closedGroupUpdate.build(), userPublicKey, timestamp) - } - val allOpenGroups = storage.getAllOpenGroups().map { it.value.server } - for (openGroup in configurationMessage.openGroups) { - if (allOpenGroups.contains(openGroup)) continue - OpenGroupUtilities.addGroup(context, openGroup, 1) - } - if (configurationMessage.displayName.isNotEmpty()) { - TextSecurePreferences.setProfileName(context, configurationMessage.displayName) - recipientDatabase.setProfileName(ourRecipient, configurationMessage.displayName) - } - if (configurationMessage.profileKey.isNotEmpty()) { - val profileKey = Base64.encodeBytes(configurationMessage.profileKey) - ProfileKeyUtil.setEncodedProfileKey(context, profileKey) - recipientDatabase.setProfileKey(ourRecipient, configurationMessage.profileKey) - if (!configurationMessage.profilePicture.isNullOrEmpty() && TextSecurePreferences.getProfilePictureURL(context) != configurationMessage.profilePicture) { - TextSecurePreferences.setProfilePictureURL(context, configurationMessage.profilePicture) - ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, configurationMessage.profilePicture)) - } - } - for (contact in configurationMessage.contacts) { - val address = Address.fromSerialized(contact.publicKey) - val recipient = Recipient.from(context, address, true) - if (!contact.profilePicture.isNullOrEmpty()) { - recipientDatabase.setProfileAvatar(recipient, contact.profilePicture) - } - if (contact.profileKey?.isNotEmpty() == true) { - recipientDatabase.setProfileKey(recipient, contact.profileKey) - } - if (contact.name.isNotEmpty()) { - recipientDatabase.setProfileName(recipient, contact.name) - } - recipientDatabase.setProfileSharing(recipient, true) - recipientDatabase.setRegistered(recipient, Recipient.RegisteredState.REGISTERED) - // create Thread if needed - threadDatabase.getOrCreateThreadIdFor(recipient) - } - if (configurationMessage.contacts.isNotEmpty()) { - threadDatabase.notifyUpdatedFromConfig() - } - } - } } \ No newline at end of file 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 fc1b28e1cc..d8d136ca2d 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 @@ -111,7 +111,7 @@ private fun MessageReceiver.handleDataExtractionNotification(message: DataExtrac // Configuration message handling -private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMessage) { +private fun handleConfigurationMessage(message: ConfigurationMessage) { val context = MessagingConfiguration.shared.context val storage = MessagingConfiguration.shared.storage if (TextSecurePreferences.getConfigurationMessageSynced(context) && !TextSecurePreferences.shouldUpdateProfile(context, message.sentTimestamp!!)) return @@ -150,25 +150,25 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS val context = MessagingConfiguration.shared.context // Update profile if needed val newProfile = message.profile - if (newProfile != null) { + if (newProfile != null && openGroupID.isNullOrEmpty()) { val profileManager = SSKEnvironment.shared.profileManager val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false) val displayName = newProfile.displayName!! val userPublicKey = storage.getUserPublicKey() - if (openGroupID == null) { - if (userPublicKey == message.sender) { - // Update the user's local name if the message came from their master device - TextSecurePreferences.setProfileName(context, displayName) - } - profileManager.setDisplayName(context, recipient, displayName) + if (userPublicKey == message.sender && displayName.isNotEmpty()) { + // Update the user's local name if the message came from their master device + TextSecurePreferences.setProfileName(context, displayName) } - if (recipient.profileKey == null || !MessageDigest.isEqual(recipient.profileKey, newProfile.profileKey)) { + profileManager.setDisplayName(context, recipient, displayName) + if (recipient.profileKey?.isNotEmpty() == true && !MessageDigest.isEqual(recipient.profileKey, newProfile.profileKey)) { profileManager.setProfileKey(context, recipient, newProfile.profileKey!!) profileManager.setUnidentifiedAccessMode(context, recipient, Recipient.UnidentifiedAccessMode.UNKNOWN) - val url = newProfile.profilePictureURL.orEmpty() - profileManager.setProfilePictureURL(context, recipient, url) - if (userPublicKey == message.sender) { - profileManager.updateOpenGroupProfilePicturesIfNeeded(context) + val newUrl = newProfile.profilePictureURL + if (!newUrl.isNullOrEmpty()) { + profileManager.setProfilePictureURL(context, recipient, newUrl) + if (userPublicKey == message.sender) { + profileManager.updateOpenGroupProfilePicturesIfNeeded(context) + } } } } From 42ca1332ec9035481ce88b94d1c15cf340795a4d Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 26 Apr 2021 15:44:31 +1000 Subject: [PATCH 34/36] fix: update prof pic and name and use new profile instead of existing recipient --- .../messaging/sending_receiving/MessageReceiverHandler.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 d8d136ca2d..cabb92b8c7 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 @@ -158,9 +158,10 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS if (userPublicKey == message.sender && displayName.isNotEmpty()) { // Update the user's local name if the message came from their master device TextSecurePreferences.setProfileName(context, displayName) + } else if (displayName.isNotEmpty()) { + profileManager.setDisplayName(context, recipient, displayName) } - profileManager.setDisplayName(context, recipient, displayName) - if (recipient.profileKey?.isNotEmpty() == true && !MessageDigest.isEqual(recipient.profileKey, newProfile.profileKey)) { + if (newProfile.profileKey?.isNotEmpty() == true && !MessageDigest.isEqual(recipient.profileKey, newProfile.profileKey)) { profileManager.setProfileKey(context, recipient, newProfile.profileKey!!) profileManager.setUnidentifiedAccessMode(context, recipient, Recipient.UnidentifiedAccessMode.UNKNOWN) val newUrl = newProfile.profilePictureURL From 96cc8675129b962e28256fe133c7b3d33fe572ad Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 26 Apr 2021 15:55:13 +1000 Subject: [PATCH 35/36] fix: BackgroundPollWorker.kt new instance instead of null instance --- .../thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt index 8476fae3e1..15d84db459 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt @@ -9,11 +9,11 @@ import nl.komponents.kovenant.all import nl.komponents.kovenant.functional.map import org.session.libsession.messaging.jobs.MessageReceiveJob import org.session.libsession.messaging.opengroups.OpenGroup +import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.service.loki.api.SnodeAPI import org.session.libsignal.utilities.logging.Log -import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.DatabaseFactory import java.util.concurrent.TimeUnit @@ -78,7 +78,7 @@ class BackgroundPollWorker(val context: Context, params: WorkerParameters) : Wor promises.addAll(privateChatsPromise.get()) // Closed groups - promises.addAll(ApplicationContext.getInstance(context).closedGroupPoller.pollOnce()) + promises.addAll(ClosedGroupPoller().pollOnce()) // Open Groups val openGroups = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().map { (_,chat)-> From 11aed5daff8b9b2a54df32fdea9b06fbd75680c4 Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 27 Apr 2021 09:38:07 +1000 Subject: [PATCH 36/36] refactor: don't update profile if it is user's at all in visible message handling, leave it to config updates --- .../sending_receiving/MessageReceiverHandler.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 cabb92b8c7..5781079830 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 @@ -148,17 +148,14 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) { fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalServiceProtos.Content, openGroupID: String?) { val storage = MessagingConfiguration.shared.storage val context = MessagingConfiguration.shared.context + val userPublicKey = storage.getUserPublicKey() // Update profile if needed val newProfile = message.profile - if (newProfile != null && openGroupID.isNullOrEmpty()) { + if (newProfile != null && openGroupID.isNullOrEmpty() && userPublicKey != message.sender) { val profileManager = SSKEnvironment.shared.profileManager val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false) val displayName = newProfile.displayName!! - val userPublicKey = storage.getUserPublicKey() - if (userPublicKey == message.sender && displayName.isNotEmpty()) { - // Update the user's local name if the message came from their master device - TextSecurePreferences.setProfileName(context, displayName) - } else if (displayName.isNotEmpty()) { + if (displayName.isNotEmpty()) { profileManager.setDisplayName(context, recipient, displayName) } if (newProfile.profileKey?.isNotEmpty() == true && !MessageDigest.isEqual(recipient.profileKey, newProfile.profileKey)) {