Add GV2 accept by PNI invite.

This commit is contained in:
Cody Henthorne
2022-07-11 15:20:00 -04:00
parent b223ebe95e
commit c4bef8099f
71 changed files with 1468 additions and 1016 deletions

View File

@@ -83,6 +83,9 @@ 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";
/** Was temporarily used for PNP accept by pni but is no longer needed/updated */
@Deprecated
private static final String AUTH_SERVICE_ID = "auth_service_id";
@@ -125,7 +128,7 @@ public class GroupDatabase extends Database {
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, AUTH_SERVICE_ID
TIMESTAMP, ACTIVE, MMS, V2_MASTER_KEY, V2_REVISION, V2_DECRYPTED_GROUP
};
static final List<String> TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList();
@@ -477,25 +480,23 @@ public class GroupDatabase extends Database {
if (groupExists(groupId.deriveV2MigrationGroupId())) {
throw new LegacyGroupInsertException(groupId);
}
create(null, groupId, title, members, avatar, relay, null, null);
create(groupId, title, members, avatar, relay, null, null);
}
public void create(@NonNull GroupId.Mms groupId,
@Nullable String title,
@NonNull Collection<RecipientId> members)
{
create(null, groupId, Util.isEmpty(title) ? null : title, members, null, null, null, null);
create(groupId, Util.isEmpty(title) ? null : title, members, null, null, null, null);
}
public GroupId.V2 create(@Nullable ServiceId authServiceId,
@NonNull GroupMasterKey groupMasterKey,
public GroupId.V2 create(@NonNull GroupMasterKey groupMasterKey,
@NonNull DecryptedGroup groupState)
{
return create(authServiceId, groupMasterKey, groupState, false);
return create(groupMasterKey, groupState, false);
}
public GroupId.V2 create(@Nullable ServiceId authServiceId,
@NonNull GroupMasterKey groupMasterKey,
public GroupId.V2 create(@NonNull GroupMasterKey groupMasterKey,
@NonNull DecryptedGroup groupState,
boolean force)
{
@@ -507,7 +508,7 @@ public class GroupDatabase extends Database {
Log.w(TAG, "Forcing the creation of a group even though we already have a V1 ID!");
}
create(authServiceId, groupId, groupState.getTitle(), Collections.emptyList(), null, null, groupMasterKey, groupState);
create(groupId, groupState.getTitle(), Collections.emptyList(), null, null, groupMasterKey, groupState);
return groupId;
}
@@ -537,8 +538,8 @@ public class GroupDatabase extends Database {
if (updated < 1) {
Log.w(TAG, "No group entry. Creating restore placeholder for " + groupId);
create(authServiceId,
groupMasterKey,
create(
groupMasterKey,
DecryptedGroup.newBuilder()
.setRevision(GroupsV2StateProcessor.RESTORE_PLACEHOLDER_REVISION)
.build(),
@@ -559,8 +560,7 @@ public class GroupDatabase extends Database {
/**
* @param groupMasterKey null for V1, must be non-null for V2 (presence dictates group version).
*/
private void create(@Nullable ServiceId authServiceId,
@NonNull GroupId groupId,
private void create(@NonNull GroupId groupId,
@Nullable String title,
@NonNull Collection<RecipientId> memberCollection,
@Nullable SignalServiceAttachmentPointer avatar,
@@ -575,7 +575,6 @@ public class GroupDatabase extends Database {
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);
@@ -987,13 +986,6 @@ public class GroupDatabase extends Database {
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;
@@ -1038,8 +1030,7 @@ public class GroupDatabase extends Database {
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.requireString(cursor, AUTH_SERVICE_ID));
CursorUtil.getString(cursor, DISTRIBUTION_ID).map(DistributionId::from).orElse(null));
}
@Override
@@ -1065,7 +1056,6 @@ public class GroupDatabase extends Database {
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,
@@ -1082,8 +1072,7 @@ public class GroupDatabase extends Database {
@Nullable byte[] groupMasterKeyBytes,
int groupRevision,
@Nullable byte[] decryptedGroupBytes,
@Nullable DistributionId distributionId,
@Nullable String authServiceId)
@Nullable DistributionId distributionId)
{
this.id = id;
this.recipientId = recipientId;
@@ -1096,7 +1085,6 @@ public class GroupDatabase extends Database {
this.active = active;
this.mms = mms;
this.distributionId = distributionId;
this.authServiceId = authServiceId;
V2GroupProperties v2GroupProperties = null;
if (groupMasterKeyBytes != null && decryptedGroupBytes != null) {
@@ -1285,10 +1273,6 @@ public class GroupDatabase extends Database {
}
return false;
}
public @Nullable ServiceId getAuthServiceId() {
return ServiceId.parseOrNull(authServiceId);
}
}
public static class V2GroupProperties {

View File

@@ -33,8 +33,8 @@ import org.signal.core.util.withinTransaction
import org.signal.libsignal.protocol.IdentityKey
import org.signal.libsignal.protocol.InvalidKeyException
import org.signal.libsignal.zkgroup.InvalidInputException
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential
import org.signal.libsignal.zkgroup.profiles.ProfileKey
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.thoughtcrime.securesms.badges.Badges
import org.thoughtcrime.securesms.badges.Badges.toDatabaseBadge
@@ -66,7 +66,7 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.BadgeList
import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor
import org.thoughtcrime.securesms.database.model.databaseprotos.DeviceLastResetTime
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileKeyCredentialColumnData
import org.thoughtcrime.securesms.database.model.databaseprotos.ExpiringProfileKeyCredentialColumnData
import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras
import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
@@ -154,7 +154,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
private const val SYSTEM_CONTACT_URI = "system_contact_uri"
private const val SYSTEM_INFO_PENDING = "system_info_pending"
private const val PROFILE_KEY = "profile_key"
private const val PROFILE_KEY_CREDENTIAL = "profile_key_credential"
const val EXPIRING_PROFILE_KEY_CREDENTIAL = "profile_key_credential"
private const val SIGNAL_PROFILE_AVATAR = "signal_profile_avatar"
const val PROFILE_SHARING = "profile_sharing"
private const val LAST_PROFILE_FETCH = "last_profile_fetch"
@@ -214,7 +214,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
$SYSTEM_CONTACT_URI TEXT DEFAULT NULL,
$SYSTEM_INFO_PENDING INTEGER DEFAULT 0,
$PROFILE_KEY TEXT DEFAULT NULL,
$PROFILE_KEY_CREDENTIAL TEXT DEFAULT NULL,
$EXPIRING_PROFILE_KEY_CREDENTIAL TEXT DEFAULT NULL,
$PROFILE_GIVEN_NAME TEXT DEFAULT NULL,
$PROFILE_FAMILY_NAME TEXT DEFAULT NULL,
$PROFILE_JOINED_NAME TEXT DEFAULT NULL,
@@ -269,7 +269,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
MESSAGE_EXPIRATION_TIME,
REGISTERED,
PROFILE_KEY,
PROFILE_KEY_CREDENTIAL,
EXPIRING_PROFILE_KEY_CREDENTIAL,
SYSTEM_JOINED_NAME,
SYSTEM_GIVEN_NAME,
SYSTEM_FAMILY_NAME,
@@ -925,7 +925,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
val recipientId = getByStorageKeyOrThrow(update.new.id.raw)
if (StorageSyncHelper.profileKeyChanged(update)) {
val clearValues = ContentValues(1).apply {
putNull(PROFILE_KEY_CREDENTIAL)
putNull(EXPIRING_PROFILE_KEY_CREDENTIAL)
}
db.update(TABLE_NAME, clearValues, ID_WHERE, SqlUtil.buildArgs(recipientId))
}
@@ -986,7 +986,6 @@ 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)
@@ -1560,7 +1559,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
}
val valuesToSet = ContentValues(3).apply {
put(PROFILE_KEY, encodedProfileKey)
putNull(PROFILE_KEY_CREDENTIAL)
putNull(EXPIRING_PROFILE_KEY_CREDENTIAL)
put(UNIDENTIFIED_ACCESS_MODE, UnidentifiedAccessMode.UNKNOWN.mode)
}
@@ -1592,7 +1591,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
val args = arrayOf(id.serialize())
val valuesToSet = ContentValues(3).apply {
put(PROFILE_KEY, Base64.encodeBytes(profileKey.serialize()))
putNull(PROFILE_KEY_CREDENTIAL)
putNull(EXPIRING_PROFILE_KEY_CREDENTIAL)
put(UNIDENTIFIED_ACCESS_MODE, UnidentifiedAccessMode.UNKNOWN.mode)
}
@@ -1611,16 +1610,16 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
fun setProfileKeyCredential(
id: RecipientId,
profileKey: ProfileKey,
profileKeyCredential: ProfileKeyCredential
expiringProfileKeyCredential: ExpiringProfileKeyCredential
): Boolean {
val selection = "$ID = ? AND $PROFILE_KEY = ?"
val args = arrayOf(id.serialize(), Base64.encodeBytes(profileKey.serialize()))
val columnData = ProfileKeyCredentialColumnData.newBuilder()
val columnData = ExpiringProfileKeyCredentialColumnData.newBuilder()
.setProfileKey(ByteString.copyFrom(profileKey.serialize()))
.setProfileKeyCredential(ByteString.copyFrom(profileKeyCredential.serialize()))
.setExpiringProfileKeyCredential(ByteString.copyFrom(expiringProfileKeyCredential.serialize()))
.build()
val values = ContentValues(1).apply {
put(PROFILE_KEY_CREDENTIAL, Base64.encodeBytes(columnData.toByteArray()))
put(EXPIRING_PROFILE_KEY_CREDENTIAL, Base64.encodeBytes(columnData.toByteArray()))
}
val updateQuery = SqlUtil.buildTrueUpdateQuery(selection, args, values)
@@ -1634,11 +1633,8 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
fun clearProfileKeyCredential(id: RecipientId) {
val values = ContentValues(1)
values.putNull(PROFILE_KEY_CREDENTIAL)
values.putNull(PROFILE_KEY)
values.put(PROFILE_SHARING, 0)
values.putNull(EXPIRING_PROFILE_KEY_CREDENTIAL)
if (update(id, values)) {
rotateStorageId(id)
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id)
}
}
@@ -2101,11 +2097,11 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
db.beginTransaction()
try {
for ((recipientId, aci) in registered) {
for ((recipientId, serviceId) in registered) {
val values = ContentValues(2).apply {
put(REGISTERED, RegisteredState.REGISTERED.id)
if (aci != null) {
put(SERVICE_ID, aci.toString().lowercase())
if (serviceId != null) {
put(SERVICE_ID, serviceId.toString().lowercase())
}
}
@@ -2117,7 +2113,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
} catch (e: SQLiteConstraintException) {
Log.w(TAG, "[bulkUpdateRegisteredStatus] Hit a conflict when trying to update $recipientId. Possibly merging.")
val e164 = getRecord(recipientId).e164
val newId = getAndPossiblyMerge(aci, e164)
val newId = getAndPossiblyMerge(serviceId, e164)
Log.w(TAG, "[bulkUpdateRegisteredStatus] Merged into $newId")
}
}
@@ -2173,7 +2169,8 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
}
/**
* Processes CDSv2 results, merging recipients as necessary.
* Processes CDSv2 results, merging recipients as necessary. Does not mark users as
* registered.
*
* Important: This is under active development and is not suitable for actual use.
*
@@ -3489,6 +3486,9 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
}
}
.run()
ApplicationDependencies.getRecipientCache().clear()
RecipientId.clearCache()
}
/**
@@ -3499,7 +3499,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
.update(TABLE_NAME)
.values(
PROFILE_KEY to null,
PROFILE_KEY_CREDENTIAL to null,
EXPIRING_PROFILE_KEY_CREDENTIAL to null,
PROFILE_GIVEN_NAME to null,
PROFILE_FAMILY_NAME to null,
PROFILE_JOINED_NAME to null,
@@ -3514,6 +3514,9 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
}
}
.run()
ApplicationDependencies.getRecipientCache().clear()
RecipientId.clearCache()
}
fun getRecord(context: Context, cursor: Cursor): RecipientRecord {
@@ -3522,9 +3525,9 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
fun getRecord(context: Context, cursor: Cursor, idColumnName: String): RecipientRecord {
val profileKeyString = cursor.requireString(PROFILE_KEY)
val profileKeyCredentialString = cursor.requireString(PROFILE_KEY_CREDENTIAL)
val expiringProfileKeyCredentialString = cursor.requireString(EXPIRING_PROFILE_KEY_CREDENTIAL)
var profileKey: ByteArray? = null
var profileKeyCredential: ProfileKeyCredential? = null
var expiringProfileKeyCredential: ExpiringProfileKeyCredential? = null
if (profileKeyString != null) {
try {
@@ -3533,12 +3536,12 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
Log.w(TAG, e)
}
if (profileKeyCredentialString != null) {
if (expiringProfileKeyCredentialString != null) {
try {
val columnDataBytes = Base64.decode(profileKeyCredentialString)
val columnData = ProfileKeyCredentialColumnData.parseFrom(columnDataBytes)
val columnDataBytes = Base64.decode(expiringProfileKeyCredentialString)
val columnData = ExpiringProfileKeyCredentialColumnData.parseFrom(columnDataBytes)
if (Arrays.equals(columnData.profileKey.toByteArray(), profileKey)) {
profileKeyCredential = ProfileKeyCredential(columnData.profileKeyCredential.toByteArray())
expiringProfileKeyCredential = ExpiringProfileKeyCredential(columnData.expiringProfileKeyCredential.toByteArray())
} else {
Log.i(TAG, "Out of date profile key credential data ignored on read")
}
@@ -3598,7 +3601,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
expireMessages = cursor.requireInt(MESSAGE_EXPIRATION_TIME),
registered = RegisteredState.fromId(cursor.requireInt(REGISTERED)),
profileKey = profileKey,
profileKeyCredential = profileKeyCredential,
expiringProfileKeyCredential = expiringProfileKeyCredential,
systemProfileName = ProfileName.fromParts(cursor.requireString(SYSTEM_GIVEN_NAME), cursor.requireString(SYSTEM_FAMILY_NAME)),
systemDisplayName = cursor.requireString(SYSTEM_JOINED_NAME),
systemContactPhotoUri = cursor.requireString(SYSTEM_PHOTO_URI),
@@ -3688,7 +3691,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
private fun updateProfileValuesForMerge(values: ContentValues, record: RecipientRecord) {
values.apply {
put(PROFILE_KEY, if (record.profileKey != null) Base64.encodeBytes(record.profileKey) else null)
putNull(PROFILE_KEY_CREDENTIAL)
putNull(EXPIRING_PROFILE_KEY_CREDENTIAL)
put(SIGNAL_PROFILE_AVATAR, record.signalProfileAvatar)
put(PROFILE_GIVEN_NAME, record.signalProfileName.givenName)
put(PROFILE_FAMILY_NAME, record.signalProfileName.familyName)

View File

@@ -202,8 +202,9 @@ object SignalDatabaseMigrations {
private const val REMOTE_MEGAPHONE = 146
private const val QUOTE_INDEX = 147
private const val MY_STORY_PRIVACY_MODE = 148
private const val EXPIRING_PROFILE_CREDENTIALS = 149
const val DATABASE_VERSION = 148
const val DATABASE_VERSION = 149
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -2658,6 +2659,10 @@ object SignalDatabaseMigrations {
db.execSQL("CREATE UNIQUE INDEX distribution_list_member_list_id_recipient_id_privacy_mode_index ON distribution_list_member (list_id, recipient_id, privacy_mode)")
}
if (oldVersion < EXPIRING_PROFILE_CREDENTIALS) {
db.execSQL("UPDATE recipient SET profile_key_credential = NULL")
}
}
@JvmStatic

View File

@@ -28,13 +28,13 @@ 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.thoughtcrime.securesms.util.SpanUtil;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceIds;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.Arrays;
@@ -91,8 +91,8 @@ final class GroupsV2UpdateMessageProducer {
}
if (DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getAci().uuid()).isPresent() ||
(selfIds.getPni() != null && DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfIds.getPni().uuid()).isPresent())
) {
(selfIds.getPni() != null && 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);
@@ -124,6 +124,7 @@ final class GroupsV2UpdateMessageProducer {
describeUnknownEditorRequestingMembersApprovals(change, updates);
describeUnknownEditorRequestingMembersDeletes(change, updates);
describeUnknownEditorAnnouncementGroupChange(change, updates);
describeUnknownEditorPromotePendingPniAci(change, updates);
describeUnknownEditorMemberRemovals(change, updates);
@@ -149,6 +150,7 @@ final class GroupsV2UpdateMessageProducer {
describeRequestingMembersApprovals(change, updates);
describeRequestingMembersDeletes(change, updates);
describeAnnouncementGroupChange(change, updates);
describePromotePendingPniAci(change, updates);
describeMemberRemovals(change, updates);
@@ -304,8 +306,8 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditor());
int notYouInviteCount = 0;
boolean editorIsYou = selfIds.matches(change.getEditor());
int notYouInviteCount = 0;
for (DecryptedPendingMember invitee : change.getNewPendingMembersList()) {
boolean newMemberIsYou = selfIds.matches(invitee.getUuid());
@@ -351,8 +353,8 @@ final class GroupsV2UpdateMessageProducer {
}
private void describeRevokedInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditor());
int notDeclineCount = 0;
boolean editorIsYou = selfIds.matches(change.getEditor());
int notDeclineCount = 0;
for (DecryptedPendingMemberRemoval invitee : change.getDeletePendingMembersList()) {
boolean decline = invitee.getUuid().equals(change.getEditor());
@@ -400,8 +402,8 @@ final class GroupsV2UpdateMessageProducer {
boolean editorIsYou = selfIds.matches(change.getEditor());
for (DecryptedMember newMember : change.getPromotePendingMembersList()) {
ByteString uuid = newMember.getUuid();
boolean newMemberIsYou = selfIds.matches(uuid);
ByteString uuid = newMember.getUuid();
boolean newMemberIsYou = selfIds.matches(uuid);
if (editorIsYou) {
if (newMemberIsYou) {
@@ -425,8 +427,8 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorPromotePending(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember newMember : change.getPromotePendingMembersList()) {
ByteString uuid = newMember.getUuid();
boolean newMemberIsYou = selfIds.matches(uuid);
ByteString uuid = newMember.getUuid();
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));
@@ -679,7 +681,7 @@ final class GroupsV2UpdateMessageProducer {
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 = selfIds.matches(change.getEditor());
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));
@@ -770,6 +772,46 @@ final class GroupsV2UpdateMessageProducer {
}
}
private void describePromotePendingPniAci(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.getEditor());
for (DecryptedMember newMember : change.getPromotePendingPniAciMembersList()) {
ByteString uuid = newMember.getUuid();
boolean newMemberIsYou = selfIds.matches(uuid);
if (editorIsYou) {
if (newMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_accepted_invite), R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_you_added_invited_member_s, uuid, R.drawable.ic_update_group_add_16));
}
} else {
if (newMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_added_you, change.getEditor(), R.drawable.ic_update_group_add_16));
} else {
if (uuid.equals(change.getEditor())) {
updates.add(updateDescription(R.string.MessageRecord_s_accepted_invite, uuid, R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, change.getEditor(), uuid, R.drawable.ic_update_group_add_16));
}
}
}
}
}
private void describeUnknownEditorPromotePendingPniAci(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
for (DecryptedMember newMember : change.getPromotePendingPniAciMembersList()) {
ByteString uuid = newMember.getUuid();
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));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, uuid, R.drawable.ic_update_group_add_16));
}
}
}
private static UpdateDescription updateDescription(@NonNull String string, @DrawableRes int iconResource) {
return UpdateDescription.staticDescription(string, iconResource);
}
@@ -868,8 +910,8 @@ final class GroupsV2UpdateMessageProducer {
@VisibleForTesting
static @NonNull Spannable makeRecipientsClickable(@NonNull Context context, @NonNull String template, @NonNull List<RecipientId> recipientIds, @Nullable Consumer<RecipientId> clickHandler) {
SpannableStringBuilder builder = new SpannableStringBuilder();
int startIndex = 0;
SpannableStringBuilder builder = new SpannableStringBuilder();
int startIndex = 0;
Map<String, RecipientId> idByPlaceholder = new HashMap<>();
for (RecipientId id : recipientIds) {

View File

@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.database.model
import android.net.Uri
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredential
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.conversation.colors.ChatColors
@@ -45,7 +45,7 @@ data class RecipientRecord(
val expireMessages: Int,
val registered: RegisteredState,
val profileKey: ByteArray?,
val profileKeyCredential: ProfileKeyCredential?,
val expiringProfileKeyCredential: ExpiringProfileKeyCredential?,
val systemProfileName: ProfileName,
val systemDisplayName: String?,
val systemContactPhotoUri: String?,