2015-11-05 18:41:43 +00:00
|
|
|
package org.thoughtcrime.securesms.groups;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
|
|
|
|
import com.google.protobuf.ByteString;
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
|
|
|
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
2017-07-26 16:59:15 +00:00
|
|
|
import org.thoughtcrime.securesms.database.Address;
|
2015-11-05 18:41:43 +00:00
|
|
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
|
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
2017-08-01 15:56:00 +00:00
|
|
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
2015-11-05 18:41:43 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
2019-02-26 01:47:30 +00:00
|
|
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
2015-11-05 18:41:43 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
|
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
|
|
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
|
|
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
2017-05-08 22:32:59 +00:00
|
|
|
import org.thoughtcrime.securesms.util.MediaUtil;
|
2015-11-05 18:41:43 +00:00
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
2016-03-23 17:34:41 +00:00
|
|
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
|
|
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
|
2015-11-05 18:41:43 +00:00
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
import java.io.IOException;
|
2015-11-05 18:41:43 +00:00
|
|
|
import java.util.Collection;
|
2018-04-27 00:03:54 +00:00
|
|
|
import java.util.Collections;
|
2015-11-05 18:41:43 +00:00
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.LinkedList;
|
2017-07-26 16:59:15 +00:00
|
|
|
import java.util.List;
|
2015-11-05 18:41:43 +00:00
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
public class GroupManager {
|
2017-07-26 16:59:15 +00:00
|
|
|
|
2019-12-11 23:07:17 +00:00
|
|
|
public static long getPublicChatThreadId(String id, @NonNull Context context) {
|
|
|
|
final String groupId = GroupUtil.getEncodedPublicChatId(id.getBytes());
|
|
|
|
return getThreadIdFromGroupId(groupId, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static long getRSSFeedThreadId(String id, @NonNull Context context) {
|
|
|
|
final String groupId = GroupUtil.getEncodedRSSFeedId(id.getBytes());
|
2019-09-10 06:49:17 +00:00
|
|
|
return getThreadIdFromGroupId(groupId, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static long getThreadIdFromGroupId(String groupId, @NonNull Context context) {
|
2019-09-11 23:59:15 +00:00
|
|
|
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
|
2019-09-10 06:49:17 +00:00
|
|
|
return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
|
|
|
|
}
|
|
|
|
|
2015-11-05 18:41:43 +00:00
|
|
|
public static @NonNull GroupActionResult createGroup(@NonNull Context context,
|
|
|
|
@NonNull Set<Recipient> members,
|
|
|
|
@Nullable Bitmap avatar,
|
2017-08-01 15:56:00 +00:00
|
|
|
@Nullable String name,
|
2019-12-12 23:25:53 +00:00
|
|
|
boolean mms,
|
|
|
|
@NonNull Set<Recipient> admins)
|
2019-08-05 06:27:37 +00:00
|
|
|
{
|
|
|
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
|
|
|
String id = GroupUtil.getEncodedId(database.allocateGroupId(), mms);
|
2019-12-12 23:25:53 +00:00
|
|
|
return createGroup(id, context, members, avatar, name, mms, admins);
|
2019-08-05 06:27:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static @NonNull GroupActionResult createGroup(@NonNull String id,
|
|
|
|
@NonNull Context context,
|
|
|
|
@NonNull Set<Recipient> members,
|
|
|
|
@Nullable Bitmap avatar,
|
|
|
|
@Nullable String name,
|
2019-12-12 23:25:53 +00:00
|
|
|
boolean mms,
|
|
|
|
@NonNull Set<Recipient> admins)
|
2015-11-05 18:41:43 +00:00
|
|
|
{
|
2017-08-22 17:44:04 +00:00
|
|
|
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
|
|
|
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
2019-08-05 06:27:37 +00:00
|
|
|
final String groupId = GroupUtil.getEncodedId(id.getBytes(), mms);
|
2017-08-22 17:44:04 +00:00
|
|
|
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
|
|
|
|
final Set<Address> memberAddresses = getMemberAddresses(members);
|
2019-12-12 23:25:53 +00:00
|
|
|
final Set<Address> adminAddresses = getMemberAddresses(admins);
|
2015-11-05 18:41:43 +00:00
|
|
|
|
2020-02-04 01:58:34 +00:00
|
|
|
String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
|
|
|
String ourNumber = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context);
|
|
|
|
|
|
|
|
memberAddresses.add(Address.fromSerialized(ourNumber));
|
2019-12-12 23:25:53 +00:00
|
|
|
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses));
|
2015-11-05 18:41:43 +00:00
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
if (!mms) {
|
|
|
|
groupDatabase.updateAvatar(groupId, avatarBytes);
|
2017-08-22 17:44:04 +00:00
|
|
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
|
2019-12-12 23:25:53 +00:00
|
|
|
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
|
2017-08-01 15:56:00 +00:00
|
|
|
} else {
|
2017-08-22 17:44:04 +00:00
|
|
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
2017-08-01 15:56:00 +00:00
|
|
|
return new GroupActionResult(groupRecipient, threadId);
|
2015-11-05 18:41:43 +00:00
|
|
|
}
|
2019-12-11 23:07:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static @NonNull GroupActionResult createPublicChatGroup(@NonNull String id,
|
|
|
|
@NonNull Context context,
|
|
|
|
@Nullable Bitmap avatar,
|
|
|
|
@Nullable String name)
|
|
|
|
{
|
|
|
|
final String groupId = GroupUtil.getEncodedPublicChatId(id.getBytes());
|
|
|
|
return createLokiGroup(groupId, context, avatar, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static @NonNull GroupActionResult createRSSFeedGroup(@NonNull String id,
|
|
|
|
@NonNull Context context,
|
|
|
|
@Nullable Bitmap avatar,
|
|
|
|
@Nullable String name)
|
|
|
|
{
|
|
|
|
final String groupId = GroupUtil.getEncodedRSSFeedId(id.getBytes());
|
|
|
|
return createLokiGroup(groupId, context, avatar, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
|
|
|
|
final Set<Address> memberAddresses = new HashSet<>();
|
|
|
|
|
|
|
|
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
|
2019-12-12 23:25:53 +00:00
|
|
|
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>());
|
2019-12-11 23:07:17 +00:00
|
|
|
|
|
|
|
groupDatabase.updateAvatar(groupId, avatarBytes);
|
|
|
|
|
|
|
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
|
|
|
return new GroupActionResult(groupRecipient, threadId);
|
2015-11-05 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static GroupActionResult updateGroup(@NonNull Context context,
|
2017-08-01 15:56:00 +00:00
|
|
|
@NonNull String groupId,
|
2015-11-05 18:41:43 +00:00
|
|
|
@NonNull Set<Recipient> members,
|
|
|
|
@Nullable Bitmap avatar,
|
2019-12-12 23:25:53 +00:00
|
|
|
@Nullable String name,
|
|
|
|
@NonNull Set<Recipient> admins)
|
2015-11-05 18:41:43 +00:00
|
|
|
throws InvalidNumberException
|
|
|
|
{
|
2017-07-26 16:59:15 +00:00
|
|
|
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
2017-08-01 15:56:00 +00:00
|
|
|
final Set<Address> memberAddresses = getMemberAddresses(members);
|
2019-12-12 23:25:53 +00:00
|
|
|
final Set<Address> adminAddresses = getMemberAddresses(admins);
|
2017-07-26 16:59:15 +00:00
|
|
|
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
2015-11-05 18:41:43 +00:00
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
|
|
|
|
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
|
2019-12-12 23:25:53 +00:00
|
|
|
groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses));
|
2015-11-05 18:41:43 +00:00
|
|
|
groupDatabase.updateTitle(groupId, name);
|
|
|
|
groupDatabase.updateAvatar(groupId, avatarBytes);
|
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
if (!GroupUtil.isMmsGroup(groupId)) {
|
2019-12-12 23:25:53 +00:00
|
|
|
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
|
2017-08-01 15:56:00 +00:00
|
|
|
} else {
|
2017-08-22 01:32:38 +00:00
|
|
|
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true);
|
2017-08-01 15:56:00 +00:00
|
|
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
|
|
|
return new GroupActionResult(groupRecipient, threadId);
|
|
|
|
}
|
2015-11-05 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
|
2017-08-01 15:56:00 +00:00
|
|
|
@NonNull String groupId,
|
2017-07-26 16:59:15 +00:00
|
|
|
@NonNull Set<Address> members,
|
2015-11-05 18:41:43 +00:00
|
|
|
@Nullable String groupName,
|
2019-12-12 23:25:53 +00:00
|
|
|
@Nullable byte[] avatar,
|
|
|
|
@NonNull Set<Address> admins)
|
2015-11-05 18:41:43 +00:00
|
|
|
{
|
2017-08-01 15:56:00 +00:00
|
|
|
try {
|
|
|
|
Attachment avatarAttachment = null;
|
|
|
|
Address groupAddress = Address.fromSerialized(groupId);
|
2017-08-22 01:32:38 +00:00
|
|
|
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
|
2017-08-01 15:56:00 +00:00
|
|
|
|
|
|
|
List<String> numbers = new LinkedList<>();
|
|
|
|
for (Address member : members) {
|
|
|
|
numbers.add(member.serialize());
|
|
|
|
}
|
|
|
|
|
2019-12-12 23:25:53 +00:00
|
|
|
List<String> adminNumbers = new LinkedList<>();
|
|
|
|
for (Address admin : admins) {
|
|
|
|
adminNumbers.add(admin.serialize());
|
|
|
|
}
|
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
|
|
|
|
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
|
|
|
|
.setType(GroupContext.Type.UPDATE)
|
2019-12-12 23:25:53 +00:00
|
|
|
.addAllMembers(numbers)
|
|
|
|
.addAllAdmins(adminNumbers);
|
2017-08-01 15:56:00 +00:00
|
|
|
if (groupName != null) groupContextBuilder.setName(groupName);
|
|
|
|
GroupContext groupContext = groupContextBuilder.build();
|
|
|
|
|
|
|
|
if (avatar != null) {
|
2019-02-26 01:47:30 +00:00
|
|
|
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
|
2019-04-17 14:21:30 +00:00
|
|
|
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null);
|
2017-08-01 15:56:00 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 08:41:05 +00:00
|
|
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, null, Collections.emptyList(), Collections.emptyList());
|
2018-01-25 03:17:44 +00:00
|
|
|
long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
|
2017-08-01 15:56:00 +00:00
|
|
|
|
|
|
|
return new GroupActionResult(groupRecipient, threadId);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new AssertionError(e);
|
2017-07-26 16:59:15 +00:00
|
|
|
}
|
2017-08-01 15:56:00 +00:00
|
|
|
}
|
2015-11-05 18:41:43 +00:00
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
private static Set<Address> getMemberAddresses(Collection<Recipient> recipients) {
|
|
|
|
final Set<Address> results = new HashSet<>();
|
|
|
|
for (Recipient recipient : recipients) {
|
|
|
|
results.add(recipient.getAddress());
|
2015-11-05 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
return results;
|
2015-11-05 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public static class GroupActionResult {
|
2017-08-01 15:56:00 +00:00
|
|
|
private Recipient groupRecipient;
|
|
|
|
private long threadId;
|
2015-11-05 18:41:43 +00:00
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
public GroupActionResult(Recipient groupRecipient, long threadId) {
|
2015-11-05 18:41:43 +00:00
|
|
|
this.groupRecipient = groupRecipient;
|
|
|
|
this.threadId = threadId;
|
|
|
|
}
|
|
|
|
|
2017-08-01 15:56:00 +00:00
|
|
|
public Recipient getGroupRecipient() {
|
2015-11-05 18:41:43 +00:00
|
|
|
return groupRecipient;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getThreadId() {
|
|
|
|
return threadId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|