session-android/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java

255 lines
12 KiB
Java
Raw Normal View History

package org.thoughtcrime.securesms.groups;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
2020-11-23 06:59:44 +01:00
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.protobuf.ByteString;
2021-01-14 00:28:07 +01:00
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
2021-01-15 05:36:30 +01:00
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment;
2021-01-13 07:11:30 +01:00
import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.providers.BlobProvider;
2021-01-13 07:11:30 +01:00
import org.session.libsession.messaging.threads.recipients.Recipient;
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.session.libsignal.service.api.util.InvalidNumberException;
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class GroupManager {
2020-05-12 08:33:04 +02:00
public static long getOpenGroupThreadID(String id, @NonNull Context context) {
2020-05-22 01:53:04 +02:00
final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes());
2020-05-12 08:33:04 +02:00
return getThreadIDFromGroupID(groupID, context);
}
2020-05-12 08:33:04 +02:00
public static long getRSSFeedThreadID(String id, @NonNull Context context) {
final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes());
return getThreadIDFromGroupID(groupID, context);
2019-09-10 08:49:17 +02:00
}
2020-05-12 08:33:04 +02:00
public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) {
2021-01-15 05:36:30 +01:00
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupID), false);
2019-09-10 08:49:17 +02:00
return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
}
public static @NonNull GroupActionResult createGroup(@NonNull Context context,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name,
boolean mms,
@NonNull Set<Recipient> admins)
2019-08-05 08:27:37 +02:00
{
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(database.allocateGroupId(), mms);
return createGroup(id, context, members, avatar, name, mms, admins);
2019-08-05 08:27:37 +02:00
}
public static @NonNull GroupActionResult createGroup(@NonNull String id,
@NonNull Context context,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name,
boolean mms,
@NonNull Set<Recipient> admins)
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
2019-08-05 08:27:37 +02:00
final String groupId = GroupUtil.getEncodedId(id.getBytes(), mms);
2021-01-15 05:36:30 +01:00
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), false);
final Set<Address> memberAddresses = getMemberAddresses(members);
final Set<Address> adminAddresses = getMemberAddresses(admins);
2020-05-22 01:53:04 +02:00
String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context);
2020-02-04 02:58:34 +01:00
2021-01-15 05:36:30 +01:00
memberAddresses.add(Address.Companion.fromSerialized(masterPublicKey));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses));
if (!mms) {
2020-10-19 06:21:15 +02:00
groupDatabase.updateProfilePicture(groupId, avatarBytes);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
} else {
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(
groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId);
}
}
2020-05-22 01:53:04 +02:00
public static @NonNull GroupActionResult createOpenGroup(@NonNull String id,
@NonNull Context context,
@Nullable Bitmap avatar,
@Nullable String name)
{
2020-05-22 01:53:04 +02:00
final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes());
2020-05-12 08:33:04 +02:00
return createLokiGroup(groupID, context, avatar, name);
}
2020-05-22 01:53:04 +02:00
public static @NonNull GroupActionResult createRSSFeed(@NonNull String id,
@NonNull Context context,
@Nullable Bitmap avatar,
@Nullable String name)
{
2020-05-12 08:33:04 +02:00
final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes());
return createLokiGroup(groupID, context, avatar, name);
}
2020-05-22 01:53:04 +02:00
private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId,
@NonNull Context context,
@Nullable Bitmap avatar,
@Nullable String name)
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
2021-01-15 05:36:30 +01:00
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), false);
final Set<Address> memberAddresses = new HashSet<>();
2021-01-15 05:36:30 +01:00
memberAddresses.add(Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>());
2020-10-19 06:21:15 +02:00
groupDatabase.updateProfilePicture(groupId, avatarBytes);
long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(
groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
2020-05-12 08:33:04 +02:00
return new GroupActionResult(groupRecipient, threadID);
}
2020-11-23 06:59:44 +01:00
public static boolean deleteGroup(@NonNull String groupId,
@NonNull Context context)
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
2021-01-15 05:36:30 +01:00
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), false);
if (!groupDatabase.getGroup(groupId).isPresent()) {
return false;
}
long threadId = threadDatabase.getThreadIdIfExistsFor(groupRecipient);
if (threadId != -1L) {
threadDatabase.deleteConversation(threadId);
}
return groupDatabase.delete(groupId);
}
public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull String groupId,
@NonNull Set<Recipient> members,
@Nullable Bitmap avatar,
@Nullable String name,
@NonNull Set<Recipient> admins)
throws InvalidNumberException
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Set<Address> memberAddresses = getMemberAddresses(members);
final Set<Address> adminAddresses = getMemberAddresses(admins);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
2021-01-15 05:36:30 +01:00
memberAddresses.add(Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses));
groupDatabase.updateTitle(groupId, name);
2020-10-19 06:21:15 +02:00
groupDatabase.updateProfilePicture(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
} else {
2021-01-15 05:36:30 +01:00
Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient);
return new GroupActionResult(groupRecipient, threadId);
}
}
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull String groupId,
@NonNull Set<Address> members,
@Nullable String groupName,
@Nullable byte[] avatar,
@NonNull Set<Address> admins)
{
try {
Attachment avatarAttachment = null;
2021-01-15 05:36:30 +01:00
Address groupAddress = Address.Companion.fromSerialized(groupId);
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
List<String> numbers = new LinkedList<>();
for (Address member : members) {
numbers.add(member.serialize());
}
List<String> adminNumbers = new LinkedList<>();
for (Address admin : admins) {
adminNumbers.add(admin.serialize());
}
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.setType(GroupContext.Type.UPDATE)
.addAllMembers(numbers)
.addAllAdmins(adminNumbers);
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);
}
2019-01-15 09:41:05 +01:00
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, 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);
}
}
private static Set<Address> getMemberAddresses(Collection<Recipient> recipients) {
final Set<Address> results = new HashSet<>();
for (Recipient recipient : recipients) {
results.add(recipient.getAddress());
}
return results;
}
public static class GroupActionResult {
private Recipient groupRecipient;
private long threadId;
public GroupActionResult(Recipient groupRecipient, long threadId) {
this.groupRecipient = groupRecipient;
this.threadId = threadId;
}
public Recipient getGroupRecipient() {
return groupRecipient;
}
public long getThreadId() {
return threadId;
}
}
}