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; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.providers.SingleUseBlobProvider; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.Recipients; 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.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; public class GroupManager { public static @NonNull GroupActionResult createGroup(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Set<Recipient> members, @Nullable Bitmap avatar, @Nullable String name) throws InvalidNumberException { final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final byte[] groupId = groupDatabase.allocateGroupId(); final Set<String> memberE164Numbers = getE164Numbers(context, members); memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context)); groupDatabase.create(groupId, name, new LinkedList<>(memberE164Numbers), null, null); groupDatabase.updateAvatar(groupId, avatarBytes); return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes); } private static Set<String> getE164Numbers(Context context, Collection<Recipient> recipients) throws InvalidNumberException { final Set<String> results = new HashSet<>(); for (Recipient recipient : recipients) { results.add(Util.canonicalizeNumber(context, recipient.getNumber())); } return results; } public static GroupActionResult updateGroup(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull byte[] groupId, @NonNull Set<Recipient> members, @Nullable Bitmap avatar, @Nullable String name) throws InvalidNumberException { final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final Set<String> memberE164Numbers = getE164Numbers(context, members); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context)); groupDatabase.updateMembers(groupId, new LinkedList<>(memberE164Numbers)); groupDatabase.updateTitle(groupId, name); groupDatabase.updateAvatar(groupId, avatarBytes); return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes); } private static GroupActionResult sendGroupUpdate(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull byte[] groupId, @NonNull Set<String> e164numbers, @Nullable String groupName, @Nullable byte[] avatar) { Attachment avatarAttachment = null; String groupRecipientId = GroupUtil.getEncodedId(groupId); Recipients groupRecipient = RecipientFactory.getRecipientsFromString(context, groupRecipientId, false); GroupContext.Builder groupContextBuilder = GroupContext.newBuilder() .setId(ByteString.copyFrom(groupId)) .setType(GroupContext.Type.UPDATE) .addAllMembers(e164numbers); if (groupName != null) groupContextBuilder.setName(groupName); GroupContext groupContext = groupContextBuilder.build(); if (avatar != null) { Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar); avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false); } OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0); long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null); return new GroupActionResult(groupRecipient, threadId); } public static class GroupActionResult { private Recipients groupRecipient; private long threadId; public GroupActionResult(Recipients groupRecipient, long threadId) { this.groupRecipient = groupRecipient; this.threadId = threadId; } public Recipients getGroupRecipient() { return groupRecipient; } public long getThreadId() { return threadId; } } }