Add ability to reject group invite by PNI.

This commit is contained in:
Cody Henthorne
2022-04-25 12:37:25 -04:00
parent e22560a794
commit 657a9c7b0a
41 changed files with 626 additions and 325 deletions

View File

@@ -13,7 +13,9 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream;
import com.google.protobuf.InvalidProtocolBufferException;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SetUtil;
import org.signal.core.util.SqlUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
@@ -33,8 +35,6 @@ import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SqlUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct;
@@ -83,6 +83,7 @@ public class GroupDatabase extends Database {
private static final String UNMIGRATED_V1_MEMBERS = "former_v1_members";
private static final String DISTRIBUTION_ID = "distribution_id";
private static final String DISPLAY_AS_STORY = "display_as_story";
private static final String AUTH_SERVICE_ID = "auth_service_id";
/* V2 Group columns */
@@ -112,18 +113,19 @@ public class GroupDatabase extends Database {
EXPECTED_V2_ID + " TEXT DEFAULT NULL, " +
UNMIGRATED_V1_MEMBERS + " TEXT DEFAULT NULL, " +
DISTRIBUTION_ID + " TEXT DEFAULT NULL, " +
DISPLAY_AS_STORY + " INTEGER DEFAULT 0);";
DISPLAY_AS_STORY + " INTEGER DEFAULT 0, " +
AUTH_SERVICE_ID + " TEXT DEFAULT NULL);";
public static final String[] CREATE_INDEXS = {
"CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");",
"CREATE UNIQUE INDEX IF NOT EXISTS group_recipient_id_index ON " + TABLE_NAME + " (" + RECIPIENT_ID + ");",
"CREATE UNIQUE INDEX IF NOT EXISTS expected_v2_id_index ON " + TABLE_NAME + " (" + EXPECTED_V2_ID + ");",
"CREATE UNIQUE INDEX IF NOT EXISTS group_distribution_id_index ON " + TABLE_NAME + "(" + DISTRIBUTION_ID + ");"
};
};
private static final String[] GROUP_PROJECTION = {
private static final String[] GROUP_PROJECTION = {
GROUP_ID, RECIPIENT_ID, TITLE, MEMBERS, UNMIGRATED_V1_MEMBERS, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
TIMESTAMP, ACTIVE, MMS, V2_MASTER_KEY, V2_REVISION, V2_DECRYPTED_GROUP
TIMESTAMP, ACTIVE, MMS, V2_MASTER_KEY, V2_REVISION, V2_DECRYPTED_GROUP, AUTH_SERVICE_ID
};
static final List<String> TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList();
@@ -459,23 +461,25 @@ private static final String[] GROUP_PROJECTION = {
if (groupExists(groupId.deriveV2MigrationGroupId())) {
throw new LegacyGroupInsertException(groupId);
}
create(groupId, title, members, avatar, relay, null, null);
create(null, groupId, title, members, avatar, relay, null, null);
}
public void create(@NonNull GroupId.Mms groupId,
@Nullable String title,
@NonNull Collection<RecipientId> members)
{
create(groupId, Util.isEmpty(title) ? null : title, members, null, null, null, null);
create(null, groupId, Util.isEmpty(title) ? null : title, members, null, null, null, null);
}
public GroupId.V2 create(@NonNull GroupMasterKey groupMasterKey,
public GroupId.V2 create(@Nullable ServiceId authServiceId,
@NonNull GroupMasterKey groupMasterKey,
@NonNull DecryptedGroup groupState)
{
return create(groupMasterKey, groupState, false);
return create(authServiceId, groupMasterKey, groupState, false);
}
public GroupId.V2 create(@NonNull GroupMasterKey groupMasterKey,
public GroupId.V2 create(@Nullable ServiceId authServiceId,
@NonNull GroupMasterKey groupMasterKey,
@NonNull DecryptedGroup groupState,
boolean force)
{
@@ -487,7 +491,7 @@ private static final String[] GROUP_PROJECTION = {
Log.w(TAG, "Forcing the creation of a group even though we already have a V1 ID!");
}
create(groupId, groupState.getTitle(), Collections.emptyList(), null, null, groupMasterKey, groupState);
create(authServiceId, groupId, groupState.getTitle(), Collections.emptyList(), null, null, groupMasterKey, groupState);
return groupId;
}
@@ -496,7 +500,7 @@ private static final String[] GROUP_PROJECTION = {
* There was a point in time where we weren't properly responding to group creates on linked devices. This would result in us having a Recipient entry for the
* group, but we'd either be missing the group entry, or that entry would be missing a master key. This method fixes this scenario.
*/
public void fixMissingMasterKey(@NonNull GroupMasterKey groupMasterKey) {
public void fixMissingMasterKey(@Nullable ServiceId authServiceId, @NonNull GroupMasterKey groupMasterKey) {
GroupId.V2 groupId = GroupId.v2(groupMasterKey);
if (getGroupV1ByExpectedV2(groupId).isPresent()) {
@@ -517,7 +521,8 @@ private static final String[] GROUP_PROJECTION = {
if (updated < 1) {
Log.w(TAG, "No group entry. Creating restore placeholder for " + groupId);
create(groupMasterKey,
create(authServiceId,
groupMasterKey,
DecryptedGroup.newBuilder()
.setRevision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
.build(),
@@ -538,7 +543,8 @@ private static final String[] GROUP_PROJECTION = {
/**
* @param groupMasterKey null for V1, must be non-null for V2 (presence dictates group version).
*/
private void create(@NonNull GroupId groupId,
private void create(@Nullable ServiceId authServiceId,
@NonNull GroupId groupId,
@Nullable String title,
@NonNull Collection<RecipientId> memberCollection,
@Nullable SignalServiceAttachmentPointer avatar,
@@ -553,6 +559,7 @@ private static final String[] GROUP_PROJECTION = {
Collections.sort(members);
ContentValues contentValues = new ContentValues();
contentValues.put(AUTH_SERVICE_ID, authServiceId != null ? authServiceId.toString() : null);
contentValues.put(RECIPIENT_ID, groupRecipientId.serialize());
contentValues.put(GROUP_ID, groupId.toString());
contentValues.put(TITLE, title);
@@ -964,6 +971,13 @@ private static final String[] GROUP_PROJECTION = {
return result;
}
public void setAuthServiceId(@Nullable ServiceId authServiceId, @NonNull GroupId groupId) {
ContentValues values = new ContentValues(1);
values.put(AUTH_SERVICE_ID, authServiceId == null ? null : authServiceId.toString());
getWritableDatabase().update(TABLE_NAME, values, GROUP_ID + " = ?", SqlUtil.buildArgs(groupId));
}
public static class Reader implements Closeable {
public final Cursor cursor;
@@ -1008,7 +1022,8 @@ private static final String[] GROUP_PROJECTION = {
CursorUtil.requireBlob(cursor, V2_MASTER_KEY),
CursorUtil.requireInt(cursor, V2_REVISION),
CursorUtil.requireBlob(cursor, V2_DECRYPTED_GROUP),
CursorUtil.getString(cursor, DISTRIBUTION_ID).map(DistributionId::from).orElse(null));
CursorUtil.getString(cursor, DISTRIBUTION_ID).map(DistributionId::from).orElse(null),
CursorUtil.requireString(cursor, AUTH_SERVICE_ID));
}
@Override
@@ -1034,6 +1049,7 @@ private static final String[] GROUP_PROJECTION = {
private final boolean mms;
@Nullable private final V2GroupProperties v2GroupProperties;
private final DistributionId distributionId;
@Nullable private final String authServiceId;
public GroupRecord(@NonNull GroupId id,
@NonNull RecipientId recipientId,
@@ -1050,7 +1066,8 @@ private static final String[] GROUP_PROJECTION = {
@Nullable byte[] groupMasterKeyBytes,
int groupRevision,
@Nullable byte[] decryptedGroupBytes,
@Nullable DistributionId distributionId)
@Nullable DistributionId distributionId,
@Nullable String authServiceId)
{
this.id = id;
this.recipientId = recipientId;
@@ -1063,6 +1080,7 @@ private static final String[] GROUP_PROJECTION = {
this.active = active;
this.mms = mms;
this.distributionId = distributionId;
this.authServiceId = authServiceId;
V2GroupProperties v2GroupProperties = null;
if (groupMasterKeyBytes != null && decryptedGroupBytes != null) {
@@ -1193,7 +1211,11 @@ private static final String[] GROUP_PROJECTION = {
public MemberLevel memberLevel(@NonNull Recipient recipient) {
if (isV2Group()) {
return requireV2GroupProperties().memberLevel(recipient);
MemberLevel memberLevel = requireV2GroupProperties().memberLevel(recipient.getServiceId());
if (recipient.isSelf() && memberLevel == MemberLevel.NOT_A_MEMBER) {
memberLevel = requireV2GroupProperties().memberLevel(Optional.ofNullable(SignalStore.account().getPni()));
}
return memberLevel;
} else if (isMms() && recipient.isSelf()) {
return MemberLevel.FULL_MEMBER;
} else {
@@ -1247,6 +1269,10 @@ private static final String[] GROUP_PROJECTION = {
}
return false;
}
public @Nullable ServiceId getAuthServiceId() {
return ServiceId.parseOrNull(authServiceId);
}
}
public static class V2GroupProperties {
@@ -1297,9 +1323,7 @@ private static final String[] GROUP_PROJECTION = {
return members.stream().filter(this::isAdmin).collect(Collectors.toList());
}
public MemberLevel memberLevel(@NonNull Recipient recipient) {
Optional<ServiceId> serviceId = recipient.getServiceId();
public MemberLevel memberLevel(@NonNull Optional<ServiceId> serviceId) {
if (!serviceId.isPresent()) {
return MemberLevel.NOT_A_MEMBER;
}

View File

@@ -953,6 +953,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
Log.i(TAG, "Creating restore placeholder for $groupId")
groups.create(
null,
masterKey,
DecryptedGroup.newBuilder()
.setRevision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
@@ -1597,9 +1598,11 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
return updated
}
private fun clearProfileKeyCredential(id: RecipientId) {
fun clearProfileKeyCredential(id: RecipientId) {
val values = ContentValues(1)
values.putNull(PROFILE_KEY_CREDENTIAL)
values.putNull(PROFILE_KEY)
values.put(PROFILE_SHARING, 0)
if (update(id, values)) {
rotateStorageId(id)
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
@@ -2938,28 +2941,45 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
/**
* Should only be used for debugging! A very destructive action that clears all known serviceIds.
*/
fun debugClearServiceIds() {
fun debugClearServiceIds(recipientId: RecipientId? = null) {
writableDatabase
.update(TABLE_NAME)
.values(
SERVICE_ID to null,
PNI_COLUMN to null
)
.where("$ID != ?", Recipient.self().id)
.run {
if (recipientId == null) {
where("$ID != ?", Recipient.self().id)
} else {
where("$ID = ?", recipientId)
}
}
.run()
}
/**
* Should only be used for debugging! A very destructive action that clears all known profile keys and credentials.
*/
fun debugClearProfileKeys() {
fun debugClearProfileData(recipientId: RecipientId? = null) {
writableDatabase
.update(TABLE_NAME)
.values(
PROFILE_KEY to null,
PROFILE_KEY_CREDENTIAL to null
PROFILE_KEY_CREDENTIAL to null,
PROFILE_GIVEN_NAME to null,
PROFILE_FAMILY_NAME to null,
PROFILE_JOINED_NAME to null,
LAST_PROFILE_FETCH to 0,
SIGNAL_PROFILE_AVATAR to null
)
.where("$ID != ?", Recipient.self().id)
.run {
if (recipientId == null) {
where("$ID != ?", Recipient.self().id)
} else {
where("$ID = ?", recipientId)
}
}
.run()
}

View File

@@ -194,8 +194,9 @@ object SignalDatabaseMigrations {
private const val CLEAN_DELETED_DISTRIBUTION_LISTS = 138
private const val REMOVE_KNOWN_UNKNOWNS = 139
private const val CDS_V2 = 140
private const val GROUP_SERVICE_ID = 141
const val DATABASE_VERSION = 140
const val DATABASE_VERSION = 141
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -2523,6 +2524,10 @@ object SignalDatabaseMigrations {
"""
)
}
if (oldVersion < GROUP_SERVICE_ID) {
db.execSQL("ALTER TABLE groups ADD COLUMN auth_service_id TEXT DEFAULT NULL")
}
}
@JvmStatic

View File

@@ -1,12 +1,8 @@
package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import android.graphics.Color;
import android.text.PrecomputedText;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
@@ -17,6 +13,7 @@ import androidx.annotation.VisibleForTesting;
import com.google.protobuf.ByteString;
import org.signal.core.util.StringUtil;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
@@ -30,16 +27,15 @@ 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.keyvalue.ServiceIds;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.signal.core.util.StringUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -55,15 +51,13 @@ import java.util.stream.Collectors;
final class GroupsV2UpdateMessageProducer {
@NonNull private final Context context;
@NonNull private final UUID selfUuid;
@NonNull private final ByteString selfUuidBytes;
@NonNull private final Context context;
@NonNull private final ServiceIds selfIds;
@Nullable private final Consumer<RecipientId> recipientClickHandler;
GroupsV2UpdateMessageProducer(@NonNull Context context, @NonNull UUID selfUuid, @Nullable Consumer<RecipientId> recipientClickHandler) {
GroupsV2UpdateMessageProducer(@NonNull Context context, @NonNull ServiceIds selfIds, @Nullable Consumer<RecipientId> recipientClickHandler) {
this.context = context;
this.selfUuid = selfUuid;
this.selfUuidBytes = UuidUtil.toByteString(selfUuid);
this.selfIds = selfIds;
this.recipientClickHandler = recipientClickHandler;
}
@@ -77,21 +71,25 @@ final class GroupsV2UpdateMessageProducer {
* When the revision of the group is 0, the change is very noisy and only the editor is useful.
*/
UpdateDescription describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange decryptedGroupChange) {
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfUuid);
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfIds.getAci().uuid());
if (!selfPending.isPresent()) {
selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfIds.getPni().uuid());
}
if (selfPending.isPresent()) {
return updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, selfPending.get().getAddedByUuid(), R.drawable.ic_update_group_add_16);
}
ByteString foundingMemberUuid = decryptedGroupChange.getEditor();
if (!foundingMemberUuid.isEmpty()) {
if (selfUuidBytes.equals(foundingMemberUuid)) {
if (selfIds.matches(foundingMemberUuid)) {
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_16);
} else {
return updateDescription(R.string.MessageRecord_s_added_you, foundingMemberUuid, R.drawable.ic_update_group_add_16);
}
}
if (DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfUuid).isPresent()) {
if (DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getAci().uuid()).isPresent() || DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getPni().uuid()).isPresent()) {
return updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16);
} else {
return updateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
@@ -163,7 +161,7 @@ final class GroupsV2UpdateMessageProducer {
* Handles case of future protocol versions where we don't know what has changed.
*/
private void describeUnknownChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16));
@@ -177,10 +175,10 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeMemberAdditions(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
for (DecryptedMember member : change.getNewMembersList()) {
boolean newMemberIsYou = member.getUuid().equals(selfUuidBytes);
boolean newMemberIsYou = selfIds.matches(member.getUuid());
if (editorIsYou) {
if (newMemberIsYou) {
@@ -204,7 +202,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorMemberAdditions(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember member : change.getNewMembersList()) {
boolean newMemberIsYou = member.getUuid().equals(selfUuidBytes);
boolean newMemberIsYou = selfIds.matches(member.getUuid());
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
@@ -215,10 +213,10 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeMemberRemovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
for (ByteString member : change.getDeleteMembersList()) {
boolean removedMemberIsYou = member.equals(selfUuidBytes);
boolean removedMemberIsYou = selfIds.matches(member);
if (editorIsYou) {
if (removedMemberIsYou) {
@@ -242,7 +240,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorMemberRemovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (ByteString member : change.getDeleteMembersList()) {
boolean removedMemberIsYou = member.equals(selfUuidBytes);
boolean removedMemberIsYou = selfIds.matches(member);
if (removedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_in_the_group), R.drawable.ic_update_group_leave_16));
@@ -253,10 +251,10 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeModifyMemberRoles(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
for (DecryptedModifyMemberRole roleChange : change.getModifyMemberRolesList()) {
boolean changedMemberIsYou = roleChange.getUuid().equals(selfUuidBytes);
boolean changedMemberIsYou = selfIds.matches(roleChange.getUuid());
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_made_s_an_admin, roleChange.getUuid(), R.drawable.ic_update_group_role_16));
@@ -284,7 +282,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorModifyMemberRoles(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedModifyMemberRole roleChange : change.getModifyMemberRolesList()) {
boolean changedMemberIsYou = roleChange.getUuid().equals(selfUuidBytes);
boolean changedMemberIsYou = selfIds.matches(roleChange.getUuid());
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
if (changedMemberIsYou) {
@@ -303,11 +301,11 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
int notYouInviteCount = 0;
for (DecryptedPendingMember invitee : change.getNewPendingMembersList()) {
boolean newMemberIsYou = invitee.getUuid().equals(selfUuidBytes);
boolean newMemberIsYou = selfIds.matches(invitee.getUuid());
if (newMemberIsYou) {
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, change.getEditor(), R.drawable.ic_update_group_add_16));
@@ -321,8 +319,7 @@ final class GroupsV2UpdateMessageProducer {
}
if (notYouInviteCount > 0) {
final int notYouInviteCountFinalCopy = notYouInviteCount;
updates.add(updateDescription(R.plurals.MessageRecord_s_invited_members, notYouInviteCountFinalCopy, change.getEditor(), notYouInviteCountFinalCopy, R.drawable.ic_update_group_add_16));
updates.add(updateDescription(R.plurals.MessageRecord_s_invited_members, notYouInviteCount, change.getEditor(), notYouInviteCount, R.drawable.ic_update_group_add_16));
}
}
@@ -330,7 +327,7 @@ final class GroupsV2UpdateMessageProducer {
int notYouInviteCount = 0;
for (DecryptedPendingMember invitee : change.getNewPendingMembersList()) {
boolean newMemberIsYou = invitee.getUuid().equals(selfUuidBytes);
boolean newMemberIsYou = selfIds.matches(invitee.getUuid());
if (newMemberIsYou) {
UUID uuid = UuidUtil.fromByteStringOrUnknown(invitee.getAddedByUuid());
@@ -351,7 +348,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeRevokedInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
int notDeclineCount = 0;
for (DecryptedPendingMemberRemoval invitee : change.getDeletePendingMembersList()) {
@@ -362,7 +359,7 @@ final class GroupsV2UpdateMessageProducer {
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
}
} else if (invitee.getUuid().equals(selfUuidBytes)) {
} else if (selfIds.matches(invitee.getUuid())) {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_invitation_to_the_group, change.getEditor(), R.drawable.ic_update_group_decline_16));
} else {
notDeclineCount++;
@@ -373,8 +370,7 @@ final class GroupsV2UpdateMessageProducer {
if (editorIsYou) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notDeclineCount, notDeclineCount), R.drawable.ic_update_group_decline_16));
} else {
final int notDeclineCountFinalCopy = notDeclineCount;
updates.add(updateDescription(R.plurals.MessageRecord_s_revoked_invites, notDeclineCountFinalCopy, change.getEditor(), notDeclineCountFinalCopy, R.drawable.ic_update_group_decline_16));
updates.add(updateDescription(R.plurals.MessageRecord_s_revoked_invites, notDeclineCount, change.getEditor(), notDeclineCount, R.drawable.ic_update_group_decline_16));
}
}
}
@@ -383,7 +379,7 @@ final class GroupsV2UpdateMessageProducer {
int notDeclineCount = 0;
for (DecryptedPendingMemberRemoval invitee : change.getDeletePendingMembersList()) {
boolean inviteeWasYou = invitee.getUuid().equals(selfUuidBytes);
boolean inviteeWasYou = selfIds.matches(invitee.getUuid());
if (inviteeWasYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_an_admin_revoked_your_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
@@ -398,11 +394,11 @@ final class GroupsV2UpdateMessageProducer {
}
private void describePromotePending(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
for (DecryptedMember newMember : change.getPromotePendingMembersList()) {
ByteString uuid = newMember.getUuid();
boolean newMemberIsYou = uuid.equals(selfUuidBytes);
boolean newMemberIsYou = selfIds.matches(uuid);
if (editorIsYou) {
if (newMemberIsYou) {
@@ -427,7 +423,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorPromotePending(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember newMember : change.getPromotePendingMembersList()) {
ByteString uuid = newMember.getUuid();
boolean newMemberIsYou = uuid.equals(selfUuidBytes);
boolean newMemberIsYou = selfIds.matches(uuid);
if (newMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
@@ -438,7 +434,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeNewTitle(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.hasNewTitle()) {
String newTitle = StringUtil.isolateBidi(change.getNewTitle().getValue());
@@ -451,7 +447,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeNewDescription(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.hasNewDescription()) {
if (editorIsYou) {
@@ -475,7 +471,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeNewAvatar(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.hasNewAvatar()) {
if (editorIsYou) {
@@ -493,7 +489,7 @@ final class GroupsV2UpdateMessageProducer {
}
void describeNewTimer(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.hasNewTimer()) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.getNewTimer().getDuration());
@@ -513,7 +509,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeNewAttributeAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.getNewAttributeAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewAttributeAccess());
@@ -533,7 +529,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeNewMembershipAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.getNewMemberAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewMemberAccess());
@@ -562,7 +558,7 @@ final class GroupsV2UpdateMessageProducer {
previousAccessControl = previousGroupState.getAccessControl().getAddFromInviteLink();
}
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
boolean groupLinkEnabled = false;
switch (change.getNewInviteLinkAccess()) {
@@ -655,7 +651,7 @@ final class GroupsV2UpdateMessageProducer {
Set<ByteString> deleteRequestingUuids = new HashSet<>(change.getDeleteRequestingMembersList());
for (DecryptedRequestingMember member : change.getNewRequestingMembersList()) {
boolean requestingMemberIsYou = member.getUuid().equals(selfUuidBytes);
boolean requestingMemberIsYou = selfIds.matches(member.getUuid());
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group), R.drawable.ic_update_group_16));
@@ -675,12 +671,12 @@ final class GroupsV2UpdateMessageProducer {
private void describeRequestingMembersApprovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedApproveMember requestingMember : change.getPromoteRequestingMembersList()) {
boolean requestingMemberIsYou = requestingMember.getUuid().equals(selfUuidBytes);
boolean requestingMemberIsYou = selfIds.matches(requestingMember.getUuid());
if (requestingMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_approved_your_request_to_join_the_group, change.getEditor(), R.drawable.ic_update_group_accept_16));
} else {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, requestingMember.getUuid(), R.drawable.ic_update_group_accept_16));
@@ -693,7 +689,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorRequestingMembersApprovals(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedApproveMember requestingMember : change.getPromoteRequestingMembersList()) {
boolean requestingMemberIsYou = requestingMember.getUuid().equals(selfUuidBytes);
boolean requestingMemberIsYou = selfIds.matches(requestingMember.getUuid());
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved), R.drawable.ic_update_group_accept_16));
@@ -704,16 +700,16 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeRequestingMembersDeletes(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
Set<ByteString> newRequestingUuids = change.getNewRequestingMembersList().stream().map(r -> r.getUuid()).collect(Collectors.toSet());
Set<ByteString> newRequestingUuids = change.getNewRequestingMembersList().stream().map(DecryptedRequestingMember::getUuid).collect(Collectors.toSet());
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
for (ByteString requestingMember : change.getDeleteRequestingMembersList()) {
if (newRequestingUuids.contains(requestingMember)) {
continue;
}
boolean requestingMemberIsYou = requestingMember.equals(selfUuidBytes);
boolean requestingMemberIsYou = selfIds.matches(requestingMember);
if (requestingMemberIsYou) {
if (editorIsYou) {
@@ -735,7 +731,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorRequestingMembersDeletes(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (ByteString requestingMember : change.getDeleteRequestingMembersList()) {
boolean requestingMemberIsYou = requestingMember.equals(selfUuidBytes);
boolean requestingMemberIsYou = selfIds.matches(requestingMember);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin), R.drawable.ic_update_group_decline_16));
@@ -746,7 +742,7 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeAnnouncementGroupChange(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
boolean editorIsYou = selfIds.matches(change.getEditor());
if (change.getNewIsAnnouncementGroup() == EnabledState.ENABLED) {
if (editorIsYou) {
@@ -858,8 +854,7 @@ final class GroupsV2UpdateMessageProducer {
}
private static @NonNull Object[] makePlaceholders(@NonNull List<RecipientId> recipientIds, @Nullable List<Object> formatArgs) {
List<String> placeholders = recipientIds.stream().map(GroupsV2UpdateMessageProducer::makePlaceholder).collect(Collectors.toList());
List<Object> args = new LinkedList<>(placeholders);
List<Object> args = recipientIds.stream().map(GroupsV2UpdateMessageProducer::makePlaceholder).collect(Collectors.toList());
if (formatArgs != null) {
args.addAll(formatArgs);

View File

@@ -33,6 +33,7 @@ import androidx.core.content.ContextCompat;
import com.annimon.stream.Stream;
import com.google.protobuf.ByteString;
import org.signal.core.util.StringUtil;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
@@ -58,7 +59,6 @@ import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.signal.core.util.StringUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId;
@@ -270,7 +270,7 @@ public abstract class MessageRecord extends DisplayRecord {
try {
byte[] decoded = Base64.decode(body);
DecryptedGroupV2Context decryptedGroupV2Context = DecryptedGroupV2Context.parseFrom(decoded);
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, SignalStore.account().requireAci().uuid(), recipientClickHandler);
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, SignalStore.account().requireServiceIds(), recipientClickHandler);
if (decryptedGroupV2Context.hasChange() && (decryptedGroupV2Context.getGroupState().getRevision() != 0 || decryptedGroupV2Context.hasPreviousGroupState())) {
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.getPreviousGroupState(), decryptedGroupV2Context.getChange()));