mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 11:20:47 +01:00
GroupId class.
This commit is contained in:
committed by
Greyson Parrelli
parent
a73a73e42c
commit
a860315587
@@ -0,0 +1,93 @@
|
||||
package org.thoughtcrime.securesms.groups;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.util.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class GroupId {
|
||||
|
||||
private static final String ENCODED_SIGNAL_GROUP_PREFIX = "__textsecure_group__!";
|
||||
private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!";
|
||||
|
||||
private final String encodedId;
|
||||
|
||||
private GroupId(@NonNull String encodedId) {
|
||||
this.encodedId = encodedId;
|
||||
}
|
||||
|
||||
public static @NonNull GroupId v1(byte[] gv1GroupIdBytes) {
|
||||
return new GroupId(ENCODED_SIGNAL_GROUP_PREFIX + Hex.toStringCondensed(gv1GroupIdBytes));
|
||||
}
|
||||
|
||||
public static @NonNull GroupId mms(byte[] mmsGroupIdBytes) {
|
||||
return new GroupId(ENCODED_MMS_GROUP_PREFIX + Hex.toStringCondensed(mmsGroupIdBytes));
|
||||
}
|
||||
|
||||
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
|
||||
try {
|
||||
if (!isEncodedGroup(encodedGroupId)) {
|
||||
throw new IOException("Invalid encoding");
|
||||
}
|
||||
|
||||
byte[] bytes = extractDecodedId(encodedGroupId);
|
||||
return isMmsGroup(encodedGroupId) ? mms(bytes) : v1(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable GroupId parseNullable(@Nullable String encodedGroupId) {
|
||||
if (encodedGroupId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parse(encodedGroupId);
|
||||
}
|
||||
|
||||
public static boolean isEncodedGroup(@NonNull String groupId) {
|
||||
return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
|
||||
}
|
||||
|
||||
private static byte[] extractDecodedId(@NonNull String encodedGroupId) throws IOException {
|
||||
return Hex.fromStringCondensed(encodedGroupId.split("!", 2)[1]);
|
||||
}
|
||||
|
||||
private static boolean isMmsGroup(@NonNull String groupId) {
|
||||
return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
|
||||
}
|
||||
|
||||
public byte[] getDecodedId() {
|
||||
try {
|
||||
return extractDecodedId(encodedId);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMmsGroup() {
|
||||
return isMmsGroup(encodedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj instanceof GroupId) {
|
||||
return ((GroupId) obj).encodedId.equals(encodedId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return encodedId.hashCode();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return encodedId;
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public final class GroupManager {
|
||||
}
|
||||
|
||||
public static GroupActionResult updateGroup(@NonNull Context context,
|
||||
@NonNull String groupId,
|
||||
@NonNull GroupId groupId,
|
||||
@NonNull Set<Recipient> members,
|
||||
@Nullable Bitmap avatar,
|
||||
@Nullable String name)
|
||||
@@ -51,7 +51,7 @@ public final class GroupManager {
|
||||
|
||||
@WorkerThread
|
||||
public static boolean leaveGroup(@NonNull Context context, @NonNull Recipient groupRecipient) {
|
||||
String groupId = groupRecipient.requireGroupId();
|
||||
GroupId groupId = groupRecipient.requireGroupId();
|
||||
|
||||
return V1GroupManager.leaveGroup(context, groupId, groupRecipient);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
@@ -63,7 +62,7 @@ public class GroupMessageProcessor {
|
||||
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
SignalServiceGroup group = message.getGroupInfo().get();
|
||||
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
|
||||
GroupId id = GroupId.v1(group.getGroupId());
|
||||
Optional<GroupRecord> record = database.getGroup(id);
|
||||
|
||||
if (record.isPresent() && group.getType() == Type.UPDATE) {
|
||||
@@ -73,7 +72,7 @@ public class GroupMessageProcessor {
|
||||
} else if (record.isPresent() && group.getType() == Type.QUIT) {
|
||||
return handleGroupLeave(context, content, group, record.get(), outgoing);
|
||||
} else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) {
|
||||
return handleGroupInfoRequest(context, content, group, record.get());
|
||||
return handleGroupInfoRequest(context, content, record.get());
|
||||
} else {
|
||||
Log.w(TAG, "Received unknown type, ignoring...");
|
||||
return null;
|
||||
@@ -86,7 +85,7 @@ public class GroupMessageProcessor {
|
||||
boolean outgoing)
|
||||
{
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
|
||||
GroupId id = GroupId.v1(group.getGroupId());
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
builder.setType(GroupContext.Type.UPDATE);
|
||||
|
||||
@@ -106,7 +105,7 @@ public class GroupMessageProcessor {
|
||||
|
||||
if (FeatureFlags.messageRequests() && (sender.isSystemContact() || sender.isProfileSharing())) {
|
||||
Log.i(TAG, "Auto-enabling profile sharing because 'adder' is trusted. contact: " + sender.isSystemContact() + ", profileSharing: " + sender.isProfileSharing());
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.external(context, id).getId(), true);
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.externalGroup(context, id).getId(), true);
|
||||
}
|
||||
|
||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||
@@ -120,7 +119,7 @@ public class GroupMessageProcessor {
|
||||
{
|
||||
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
|
||||
GroupId id = GroupId.v1(group.getGroupId());
|
||||
|
||||
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
|
||||
Set<RecipientId> messageMembers = new HashSet<>();
|
||||
@@ -178,13 +177,12 @@ public class GroupMessageProcessor {
|
||||
|
||||
private static Long handleGroupInfoRequest(@NonNull Context context,
|
||||
@NonNull SignalServiceContent content,
|
||||
@NonNull SignalServiceGroup group,
|
||||
@NonNull GroupRecord record)
|
||||
{
|
||||
Recipient sender = Recipient.externalPush(context, content.getSender());
|
||||
|
||||
if (record.getMembers().contains(sender.getId())) {
|
||||
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), group.getGroupId()));
|
||||
ApplicationDependencies.getJobManager().add(new PushGroupUpdateJob(sender.getId(), record.getId()));
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -197,7 +195,7 @@ public class GroupMessageProcessor {
|
||||
boolean outgoing)
|
||||
{
|
||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||
String id = GroupUtil.getEncodedId(group.getGroupId(), false);
|
||||
GroupId id = GroupId.v1(group.getGroupId());
|
||||
List<RecipientId> members = record.getMembers();
|
||||
|
||||
GroupContext.Builder builder = createGroupContext(group);
|
||||
@@ -222,13 +220,13 @@ public class GroupMessageProcessor {
|
||||
{
|
||||
if (group.getAvatar().isPresent()) {
|
||||
ApplicationDependencies.getJobManager()
|
||||
.add(new AvatarDownloadJob(group.getGroupId()));
|
||||
.add(new AvatarDownloadJob(GroupId.v1(group.getGroupId())));
|
||||
}
|
||||
|
||||
try {
|
||||
if (outgoing) {
|
||||
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
||||
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupUtil.getEncodedId(group.getGroupId(), false));
|
||||
RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1(group.getGroupId()));
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
@@ -240,7 +238,7 @@ public class GroupMessageProcessor {
|
||||
} else {
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
String body = Base64.encodeBytes(storage.toByteArray());
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(GroupUtil.getEncodedId(group.getGroupId(), false)), 0, content.isNeedsReceipt());
|
||||
IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(GroupId.v1(group.getGroupId())), 0, content.isNeedsReceipt());
|
||||
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
|
||||
|
||||
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
@@ -11,7 +12,6 @@ import com.google.protobuf.ByteString;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||
import org.thoughtcrime.securesms.blurhash.BlurHash;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
@@ -28,12 +28,10 @@ import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -49,7 +47,7 @@ final class V1GroupManager {
|
||||
{
|
||||
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
||||
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
final String groupId = GroupUtil.getEncodedId(groupDatabase.allocateGroupId(), mms);
|
||||
final GroupId groupId = GroupDatabase.allocateGroupId(mms);
|
||||
final RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||
final Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||
|
||||
@@ -67,7 +65,7 @@ final class V1GroupManager {
|
||||
}
|
||||
|
||||
static GroupActionResult updateGroup(@NonNull Context context,
|
||||
@NonNull String groupId,
|
||||
@NonNull GroupId groupId,
|
||||
@NonNull Set<RecipientId> memberAddresses,
|
||||
@Nullable Bitmap avatar,
|
||||
@Nullable String name)
|
||||
@@ -81,7 +79,7 @@ final class V1GroupManager {
|
||||
groupDatabase.updateTitle(groupId, name);
|
||||
groupDatabase.updateAvatar(groupId, avatarBytes);
|
||||
|
||||
if (!GroupUtil.isMmsGroup(groupId)) {
|
||||
if (!groupId.isMmsGroup()) {
|
||||
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
|
||||
} else {
|
||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||
@@ -92,48 +90,44 @@ final class V1GroupManager {
|
||||
}
|
||||
|
||||
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
|
||||
@NonNull String groupId,
|
||||
@NonNull GroupId groupId,
|
||||
@NonNull Set<RecipientId> members,
|
||||
@Nullable String groupName,
|
||||
@Nullable byte[] avatar)
|
||||
{
|
||||
try {
|
||||
Attachment avatarAttachment = null;
|
||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||
Attachment avatarAttachment = null;
|
||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
|
||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||
|
||||
List<GroupContext.Member> uuidMembers = new LinkedList<>();
|
||||
List<String> e164Members = new LinkedList<>();
|
||||
List<GroupContext.Member> uuidMembers = new LinkedList<>();
|
||||
List<String> e164Members = new LinkedList<>();
|
||||
|
||||
for (RecipientId member : members) {
|
||||
Recipient recipient = Recipient.resolved(member);
|
||||
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
|
||||
}
|
||||
|
||||
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
|
||||
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
|
||||
.setType(GroupContext.Type.UPDATE)
|
||||
.addAllMembersE164(e164Members)
|
||||
.addAllMembers(uuidMembers);
|
||||
if (groupName != null) groupContextBuilder.setName(groupName);
|
||||
GroupContext groupContext = groupContextBuilder.build();
|
||||
|
||||
if (avatar != null) {
|
||||
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
|
||||
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null, null, null);
|
||||
}
|
||||
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
|
||||
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
for (RecipientId member : members) {
|
||||
Recipient recipient = Recipient.resolved(member);
|
||||
uuidMembers.add(GroupMessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
|
||||
}
|
||||
|
||||
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
|
||||
.setId(ByteString.copyFrom(groupId.getDecodedId()))
|
||||
.setType(GroupContext.Type.UPDATE)
|
||||
.addAllMembersE164(e164Members)
|
||||
.addAllMembers(uuidMembers);
|
||||
if (groupName != null) groupContextBuilder.setName(groupName);
|
||||
GroupContext groupContext = groupContextBuilder.build();
|
||||
|
||||
if (avatar != null) {
|
||||
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
|
||||
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null, null, null);
|
||||
}
|
||||
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());
|
||||
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
|
||||
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
static boolean leaveGroup(@NonNull Context context, @NonNull String groupId, @NonNull Recipient groupRecipient) {
|
||||
static boolean leaveGroup(@NonNull Context context, @NonNull GroupId groupId, @NonNull Recipient groupRecipient) {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user