Signal-Android/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberRepository.java

173 lines
6.7 KiB
Java

package org.thoughtcrime.securesms.groups.ui.pendingmemberinvites;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.UuidCiphertext;
import org.signal.zkgroup.util.UUIDUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.groups.GroupChangeException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupProtoUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Repository for modifying the pending members on a single group.
*/
final class PendingMemberRepository {
private static final String TAG = Log.tag(PendingMemberRepository.class);
private final Context context;
private final GroupId.V2 groupId;
private final Executor executor;
PendingMemberRepository(@NonNull Context context, @NonNull GroupId.V2 groupId) {
this.context = context.getApplicationContext();
this.executor = SignalExecutors.BOUNDED;
this.groupId = groupId;
}
public void getInvitees(@NonNull Consumer<InviteeResult> onInviteesLoaded) {
executor.execute(() -> {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
GroupDatabase.V2GroupProperties v2GroupProperties = groupDatabase.getGroup(groupId).get().requireV2GroupProperties();
DecryptedGroup decryptedGroup = v2GroupProperties.getDecryptedGroup();
List<DecryptedPendingMember> pendingMembersList = decryptedGroup.getPendingMembersList();
List<SinglePendingMemberInvitedByYou> byMe = new ArrayList<>(pendingMembersList.size());
List<MultiplePendingMembersInvitedByAnother> byOthers = new ArrayList<>(pendingMembersList.size());
ByteString self = ByteString.copyFrom(UUIDUtil.serialize(Recipient.self().getUuid().get()));
boolean selfIsAdmin = v2GroupProperties.isAdmin(Recipient.self());
Stream.of(pendingMembersList)
.groupBy(DecryptedPendingMember::getAddedByUuid)
.forEach(g ->
{
ByteString inviterUuid = g.getKey();
List<DecryptedPendingMember> invitedMembers = g.getValue();
if (self.equals(inviterUuid)) {
for (DecryptedPendingMember pendingMember : invitedMembers) {
try {
Recipient invitee = GroupProtoUtil.pendingMemberToRecipient(context, pendingMember);
UuidCiphertext uuidCipherText = new UuidCiphertext(pendingMember.getUuidCipherText().toByteArray());
byMe.add(new SinglePendingMemberInvitedByYou(invitee, uuidCipherText));
} catch (InvalidInputException e) {
Log.w(TAG, e);
}
}
} else {
Recipient inviter = GroupProtoUtil.uuidByteStringToRecipient(context, inviterUuid);
ArrayList<UuidCiphertext> uuidCipherTexts = new ArrayList<>(invitedMembers.size());
for (DecryptedPendingMember pendingMember : invitedMembers) {
try {
uuidCipherTexts.add(new UuidCiphertext(pendingMember.getUuidCipherText().toByteArray()));
} catch (InvalidInputException e) {
Log.w(TAG, e);
}
}
byOthers.add(new MultiplePendingMembersInvitedByAnother(inviter, uuidCipherTexts));
}
}
);
onInviteesLoaded.accept(new InviteeResult(byMe, byOthers, selfIsAdmin));
});
}
@WorkerThread
boolean revokeInvites(@NonNull Collection<UuidCiphertext> uuidCipherTexts) {
try {
GroupManager.revokeInvites(context, groupId, uuidCipherTexts);
return true;
} catch (GroupChangeException | IOException e) {
Log.w(TAG, e);
return false;
}
}
public static final class InviteeResult {
private final List<SinglePendingMemberInvitedByYou> byMe;
private final List<MultiplePendingMembersInvitedByAnother> byOthers;
private final boolean canRevokeInvites;
private InviteeResult(List<SinglePendingMemberInvitedByYou> byMe,
List<MultiplePendingMembersInvitedByAnother> byOthers,
boolean canRevokeInvites)
{
this.byMe = byMe;
this.byOthers = byOthers;
this.canRevokeInvites = canRevokeInvites;
}
public List<SinglePendingMemberInvitedByYou> getByMe() {
return byMe;
}
public List<MultiplePendingMembersInvitedByAnother> getByOthers() {
return byOthers;
}
public boolean isCanRevokeInvites() {
return canRevokeInvites;
}
}
public final static class SinglePendingMemberInvitedByYou {
private final Recipient invitee;
private final UuidCiphertext inviteeCipherText;
private SinglePendingMemberInvitedByYou(@NonNull Recipient invitee, @NonNull UuidCiphertext inviteeCipherText) {
this.invitee = invitee;
this.inviteeCipherText = inviteeCipherText;
}
public Recipient getInvitee() {
return invitee;
}
public UuidCiphertext getInviteeCipherText() {
return inviteeCipherText;
}
}
public final static class MultiplePendingMembersInvitedByAnother {
private final Recipient inviter;
private final Collection<UuidCiphertext> uuidCipherTexts;
private MultiplePendingMembersInvitedByAnother(@NonNull Recipient inviter, @NonNull Collection<UuidCiphertext> uuidCipherTexts) {
this.inviter = inviter;
this.uuidCipherTexts = uuidCipherTexts;
}
public Recipient getInviter() {
return inviter;
}
public Collection<UuidCiphertext> getUuidCipherTexts() {
return uuidCipherTexts;
}
}
}