Add support for announcement groups.

This commit is contained in:
Greyson Parrelli
2021-07-23 16:22:08 -04:00
parent 1a56924a56
commit 25234496bf
74 changed files with 1109 additions and 208 deletions

View File

@@ -18,6 +18,7 @@ import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.EnabledState;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.crypto.SenderKeyUtil;
@@ -56,6 +57,8 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public final class GroupDatabase extends Database {
@@ -1037,10 +1040,18 @@ private static final String[] GROUP_PROJECTION = {
}
public @NonNull String getDescription() {
if (v2GroupProperties == null) {
return "";
} else {
if (v2GroupProperties != null) {
return v2GroupProperties.getDecryptedGroup().getDescription();
} else {
return "";
}
}
public boolean isAnnouncementGroup() {
if (v2GroupProperties != null) {
return v2GroupProperties.getDecryptedGroup().getIsAnnouncementGroup() == EnabledState.ENABLED;
} else {
return false;
}
}
@@ -1048,6 +1059,15 @@ private static final String[] GROUP_PROJECTION = {
return members;
}
@WorkerThread
public @NonNull List<Recipient> getAdmins() {
if (v2GroupProperties != null) {
return v2GroupProperties.getAdmins(members.stream().map(Recipient::resolved).collect(Collectors.toList()));
} else {
return Collections.emptyList();
}
}
/** V1 members that were lost during the V1->V2 migration */
public @NonNull List<RecipientId> getUnmigratedV1Members() {
return unmigratedV1Members;
@@ -1211,6 +1231,10 @@ private static final String[] GROUP_PROJECTION = {
.or(false);
}
public @NonNull List<Recipient> getAdmins(@NonNull List<Recipient> members) {
return members.stream().filter(this::isAdmin).collect(Collectors.toList());
}
public MemberLevel memberLevel(@NonNull Recipient recipient) {
Optional<UUID> uuid = recipient.getUuid();

View File

@@ -166,6 +166,7 @@ public class RecipientDatabase extends Database {
static final int GROUPS_V2 = 0;
static final int GROUPS_V1_MIGRATION = 1;
static final int SENDER_KEY = 2;
static final int ANNOUNCEMENT_GROUPS = 3;
}
private static final String[] RECIPIENT_PROJECTION = new String[] {
@@ -544,6 +545,7 @@ public class RecipientDatabase extends Database {
if (remapped != null) {
Recipient.live(remapped.first()).refresh(remapped.second());
ApplicationDependencies.getRecipientCache().remap(remapped.first(), remapped.second());
}
if (recipientNeedingRefresh != null || remapped != null) {
@@ -1614,6 +1616,7 @@ public class RecipientDatabase extends Database {
value = Bitmask.update(value, Capabilities.GROUPS_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGv2()).serialize());
value = Bitmask.update(value, Capabilities.GROUPS_V1_MIGRATION, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGv1Migration()).serialize());
value = Bitmask.update(value, Capabilities.SENDER_KEY, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isSenderKey()).serialize());
value = Bitmask.update(value, Capabilities.ANNOUNCEMENT_GROUPS, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isAnnouncementGroup()).serialize());
ContentValues values = new ContentValues(1);
values.put(CAPABILITIES, value);
@@ -3145,6 +3148,7 @@ public class RecipientDatabase extends Database {
private final Recipient.Capability groupsV2Capability;
private final Recipient.Capability groupsV1MigrationCapability;
private final Recipient.Capability senderKeyCapability;
private final Recipient.Capability announcementGroupCapability;
private final InsightsBannerTier insightsBannerTier;
private final byte[] storageId;
private final MentionSetting mentionSetting;
@@ -3236,6 +3240,7 @@ public class RecipientDatabase extends Database {
this.groupsV2Capability = Recipient.Capability.deserialize((int) Bitmask.read(capabilities, Capabilities.GROUPS_V2, Capabilities.BIT_LENGTH));
this.groupsV1MigrationCapability = Recipient.Capability.deserialize((int) Bitmask.read(capabilities, Capabilities.GROUPS_V1_MIGRATION, Capabilities.BIT_LENGTH));
this.senderKeyCapability = Recipient.Capability.deserialize((int) Bitmask.read(capabilities, Capabilities.SENDER_KEY, Capabilities.BIT_LENGTH));
this.announcementGroupCapability = Recipient.Capability.deserialize((int) Bitmask.read(capabilities, Capabilities.ANNOUNCEMENT_GROUPS, Capabilities.BIT_LENGTH));
this.insightsBannerTier = insightsBannerTier;
this.storageId = storageId;
this.mentionSetting = mentionSetting;
@@ -3389,6 +3394,10 @@ public class RecipientDatabase extends Database {
return senderKeyCapability;
}
public @NonNull Recipient.Capability getAnnouncementGroupCapability() {
return announcementGroupCapability;
}
public @Nullable byte[] getStorageId() {
return storageId;
}

View File

@@ -19,6 +19,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval;
import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
import org.signal.storageservice.protos.groups.local.EnabledState;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.groups.GV2AccessLevelUtil;
import org.thoughtcrime.securesms.util.ExpirationUtil;
@@ -107,6 +108,7 @@ final class GroupsV2UpdateMessageProducer {
describeRequestingMembers(change, updates);
describeUnknownEditorRequestingMembersApprovals(change, updates);
describeUnknownEditorRequestingMembersDeletes(change, updates);
describeUnknownEditorAnnouncementGroupChange(change, updates);
describeUnknownEditorMemberRemovals(change, updates);
@@ -131,6 +133,7 @@ final class GroupsV2UpdateMessageProducer {
describeRequestingMembers(change, updates);
describeRequestingMembersApprovals(change, updates);
describeRequestingMembersDeletes(change, updates);
describeAnnouncementGroupChange(change, updates);
describeMemberRemovals(change, updates);
@@ -712,6 +715,32 @@ final class GroupsV2UpdateMessageProducer {
}
}
private void describeAnnouncementGroupChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
if (change.getNewIsAnnouncementGroup() == EnabledState.ENABLED) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_allow_only_admins_to_send, editor), R.drawable.ic_update_group_role_16));
}
} else if (change.getNewIsAnnouncementGroup() == EnabledState.DISABLED) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_allow_all_members_to_send, editor), R.drawable.ic_update_group_role_16));
}
}
}
private void describeUnknownEditorAnnouncementGroupChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.getNewIsAnnouncementGroup() == EnabledState.ENABLED) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
} else if (change.getNewIsAnnouncementGroup() == EnabledState.DISABLED) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
}
}
interface DescribeMemberStrategy {
/**