redesign of group update messages management

This commit is contained in:
Brice-W 2021-04-14 16:37:04 +10:00
parent df0ca11ec5
commit 9cdcdc43a6
16 changed files with 117 additions and 141 deletions

View File

@ -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;
}

View File

@ -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()) {

View File

@ -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;

View File

@ -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<String>, admins: Collection<String>, 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<String>, admins: Collection<String>, 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<String>, admins: Collection<String>, threadID: Long, sentTimestamp: Long) {
override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, 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

View File

@ -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;

View File

@ -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

View File

@ -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<SignalServiceGroup> 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,

View File

@ -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<String>, admins: Collection<String>, 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<String>, admins: Collection<String>, threadID: Long, sentTimestamp: Long)
fun isClosedGroup(publicKey: String): Boolean
fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList<ECKeyPair>

View File

@ -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

View File

@ -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;
}
}

View File

@ -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<Attachment>(), 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

View File

@ -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<Attachment> avatar,
long sentTimeMillis,
long expiresIn,
@ -31,30 +32,15 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
@NonNull List<LinkPreview> 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<Contact> contacts,
@NonNull List<LinkPreview> previews)
{
super(recipient, Base64.encodeBytes(group.toByteArray()),
new LinkedList<Attachment>() {{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<Contact> contacts,
@NonNull List<LinkPreview> previews)
{
super(recipient, Base64.encodeBytes(group.toByteArray()),
super(recipient, body,
new LinkedList<Attachment>() {{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;
}
}

View File

@ -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!!)
}
}

View File

@ -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<String>): Promise<Str
storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey)
// Notify the user
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, name, members, admins, threadID, sentTime)
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, threadID, sentTime)
// Notify the PN server
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
// Fulfill the promise
@ -107,7 +108,7 @@ fun MessageSender.setName(groupPublicKey: String, newName: String) {
// Update the group
storage.updateTitle(groupID, newName)
// Notify the user
val infoType = SignalServiceProtos.GroupContext.Type.UPDATE
val infoType = SignalServiceGroup.Type.NAME_CHANGE
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
storage.insertOutgoingInfoMessage(context, groupID, infoType, newName, members, admins, threadID, sentTime)
}
@ -150,9 +151,9 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List<String>)
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<String>) {
@ -189,9 +190,9 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List<St
generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMembers)
}
// Notify the user
val infoType = SignalServiceProtos.GroupContext.Type.UPDATE
val infoType = SignalServiceGroup.Type.MEMBER_REMOVED
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
storage.insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime)
storage.insertOutgoingInfoMessage(context, groupID, infoType, name, membersToRemove, admins, threadID, sentTime)
}
fun MessageSender.leave(groupPublicKey: String, notifyUser: Boolean = true): Promise<Unit, Exception> {
@ -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)

View File

@ -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)
*/
}

View File

@ -38,7 +38,7 @@ public class SignalServiceGroup {
DELIVER,
QUIT,
REQUEST_INFO,
NEW_GROUP,
CREATION,
NAME_CHANGE,
MEMBER_ADDED,
MEMBER_REMOVED