mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 18:00:02 +01:00
Add ability to reject group invite by PNI.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()));
|
||||
|
||||
Reference in New Issue
Block a user