mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 16:49:40 +01:00
Add support for announcement groups.
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user