Ensure all group recipients have group records.

This commit is contained in:
Greyson Parrelli
2023-08-10 13:52:11 -04:00
committed by Alex Hart
parent ec51268439
commit c7dabe1b6f
5 changed files with 346 additions and 332 deletions

View File

@@ -1846,19 +1846,9 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val recipient: Recipient = if (recipientSettings.groupId != null) {
GroupTable.Reader(cursor).getCurrent()?.let { group ->
val details = RecipientDetails(
group.title,
null,
if (group.hasAvatar()) Optional.of(group.avatarId) else Optional.empty(),
false,
false,
recipientSettings.registered,
recipientSettings,
null,
false,
group.isActive,
null,
Optional.of(group)
val details = RecipientDetails.forGroup(
groupRecord = group,
recipientRecord = recipientSettings
)
Recipient(recipientId, details, false)
} ?: Recipient.live(recipientId).get()

View File

@@ -212,18 +212,10 @@ public final class LiveRecipient {
Optional<GroupRecord> groupRecord = groupDatabase.getGroup(record.getId());
if (groupRecord.isPresent()) {
String title = groupRecord.get().getTitle();
List<RecipientId> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).toList();
Optional<Long> avatarId = Optional.empty();
if (groupRecord.get().hasAvatar()) {
avatarId = Optional.of(groupRecord.get().getAvatarId());
}
return new RecipientDetails(title, null, avatarId, false, false, record.getRegistered(), record, members, false, groupRecord.get().isActive(), null, groupRecord);
return RecipientDetails.forGroup(groupRecord.get(), record);
} else {
return RecipientDetails.forUnknown();
}
return new RecipientDetails(null, null, Optional.empty(), false, false, record.getRegistered(), record, null, false, false, null, Optional.empty());
}
@WorkerThread

View File

@@ -1,240 +0,0 @@
package org.thoughtcrime.securesms.recipients;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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;
import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting;
import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState;
import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode;
import org.thoughtcrime.securesms.database.RecipientTable.VibrateState;
import org.thoughtcrime.securesms.database.model.DistributionListId;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails;
import org.thoughtcrime.securesms.database.model.RecipientRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId.PNI;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
public class RecipientDetails {
final ACI aci;
final PNI pni;
final String username;
final String e164;
final String email;
final GroupId groupId;
final DistributionListId distributionListId;
final String groupName;
final String systemContactName;
final String customLabel;
final Uri systemContactPhoto;
final Uri contactUri;
final Optional<Long> groupAvatarId;
final Uri messageRingtone;
final Uri callRingtone;
final long mutedUntil;
final VibrateState messageVibrateState;
final VibrateState callVibrateState;
final boolean blocked;
final int expireMessages;
final List<RecipientId> participantIds;
final ProfileName profileName;
final RegisteredState registered;
final byte[] profileKey;
final ExpiringProfileKeyCredential expiringProfileKeyCredential;
final String profileAvatar;
final ProfileAvatarFileDetails profileAvatarFileDetails;
final boolean profileSharing;
final Recipient.HiddenState hiddenState;
final boolean isActiveGroup;
final long lastProfileFetch;
final boolean systemContact;
final boolean isSelf;
final String notificationChannel;
final UnidentifiedAccessMode unidentifiedAccessMode;
final RecipientRecord.Capabilities capabilities;
final byte[] storageId;
final MentionSetting mentionSetting;
final ChatWallpaper wallpaper;
final ChatColors chatColors;
final AvatarColor avatarColor;
final String about;
final String aboutEmoji;
final ProfileName systemProfileName;
final Optional<Recipient.Extras> extras;
final boolean hasGroupsInCommon;
final List<Badge> badges;
final boolean isReleaseChannel;
final boolean needsPniSignature;
final CallLinkRoomId callLinkRoomId;
final Optional<GroupRecord> groupRecord;
public RecipientDetails(@Nullable String groupName,
@Nullable String systemContactName,
@NonNull Optional<Long> groupAvatarId,
boolean systemContact,
boolean isSelf,
@NonNull RegisteredState registeredState,
@NonNull RecipientRecord record,
@Nullable List<RecipientId> participantIds,
boolean isReleaseChannel,
boolean isActiveGroup,
@Nullable AvatarColor avatarColor,
Optional<GroupRecord> groupRecord)
{
this.groupAvatarId = groupAvatarId;
this.systemContactPhoto = Util.uri(record.getSystemContactPhotoUri());
this.customLabel = record.getSystemPhoneLabel();
this.contactUri = Util.uri(record.getSystemContactUri());
this.aci = record.getAci();
this.pni = record.getPni();
this.username = record.getUsername();
this.e164 = record.getE164();
this.email = record.getEmail();
this.groupId = record.getGroupId();
this.distributionListId = record.getDistributionListId();
this.messageRingtone = record.getMessageRingtone();
this.callRingtone = record.getCallRingtone();
this.mutedUntil = record.getMuteUntil();
this.messageVibrateState = record.getMessageVibrateState();
this.callVibrateState = record.getCallVibrateState();
this.blocked = record.isBlocked();
this.expireMessages = record.getExpireMessages();
this.participantIds = participantIds == null ? new LinkedList<>() : participantIds;
this.isActiveGroup = isActiveGroup;
this.profileName = record.getProfileName();
this.registered = registeredState;
this.profileKey = record.getProfileKey();
this.expiringProfileKeyCredential = record.getExpiringProfileKeyCredential();
this.profileAvatar = record.getProfileAvatar();
this.profileAvatarFileDetails = record.getProfileAvatarFileDetails();
this.profileSharing = record.isProfileSharing();
this.hiddenState = record.getHiddenState();
this.lastProfileFetch = record.getLastProfileFetch();
this.systemContact = systemContact;
this.isSelf = isSelf;
this.notificationChannel = record.getNotificationChannel();
this.unidentifiedAccessMode = record.getUnidentifiedAccessMode();
this.capabilities = record.getCapabilities();
this.storageId = record.getStorageId();
this.mentionSetting = record.getMentionSetting();
this.wallpaper = record.getWallpaper();
this.chatColors = record.getChatColors();
this.avatarColor = avatarColor != null ? avatarColor : record.getAvatarColor();
this.about = record.getAbout();
this.aboutEmoji = record.getAboutEmoji();
this.systemProfileName = record.getSystemProfileName();
this.groupName = groupName;
this.systemContactName = systemContactName;
this.extras = Optional.ofNullable(record.getExtras());
this.hasGroupsInCommon = record.hasGroupsInCommon();
this.badges = record.getBadges();
this.isReleaseChannel = isReleaseChannel;
this.needsPniSignature = record.needsPniSignature();
this.callLinkRoomId = record.getCallLinkRoomId();
this.groupRecord = groupRecord;
}
private RecipientDetails() {
this.groupAvatarId = null;
this.systemContactPhoto = null;
this.customLabel = null;
this.contactUri = null;
this.aci = null;
this.pni = null;
this.username = null;
this.e164 = null;
this.email = null;
this.groupId = null;
this.distributionListId = null;
this.messageRingtone = null;
this.callRingtone = null;
this.mutedUntil = 0;
this.messageVibrateState = VibrateState.DEFAULT;
this.callVibrateState = VibrateState.DEFAULT;
this.blocked = false;
this.expireMessages = 0;
this.participantIds = new LinkedList<>();
this.profileName = ProfileName.EMPTY;
this.registered = RegisteredState.UNKNOWN;
this.profileKey = null;
this.expiringProfileKeyCredential = null;
this.profileAvatar = null;
this.profileAvatarFileDetails = ProfileAvatarFileDetails.NO_DETAILS;
this.profileSharing = false;
this.hiddenState = Recipient.HiddenState.NOT_HIDDEN;
this.lastProfileFetch = 0;
this.systemContact = true;
this.isSelf = false;
this.notificationChannel = null;
this.unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
this.groupName = null;
this.capabilities = RecipientRecord.Capabilities.UNKNOWN;
this.storageId = null;
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
this.wallpaper = null;
this.chatColors = null;
this.avatarColor = AvatarColor.UNKNOWN;
this.about = null;
this.aboutEmoji = null;
this.systemProfileName = ProfileName.EMPTY;
this.systemContactName = null;
this.extras = Optional.empty();
this.hasGroupsInCommon = false;
this.badges = Collections.emptyList();
this.isReleaseChannel = false;
this.needsPniSignature = false;
this.isActiveGroup = false;
this.callLinkRoomId = null;
this.groupRecord = Optional.empty();
}
public static @NonNull RecipientDetails forIndividual(@NonNull Context context, @NonNull RecipientRecord settings) {
boolean systemContact = !settings.getSystemProfileName().isEmpty();
boolean isSelf = (settings.getE164() != null && settings.getE164().equals(SignalStore.account().getE164())) ||
(settings.getAci() != null && settings.getAci().equals(SignalStore.account().getAci()));
boolean isReleaseChannel = settings.getId().equals(SignalStore.releaseChannelValues().getReleaseChannelRecipientId());
RegisteredState registeredState = settings.getRegistered();
if (isSelf) {
if (SignalStore.account().isRegistered() && !TextSecurePreferences.isUnauthorizedReceived(context)) {
registeredState = RegisteredState.REGISTERED;
} else {
registeredState = RegisteredState.NOT_REGISTERED;
}
}
return new RecipientDetails(null, settings.getSystemDisplayName(), Optional.empty(), systemContact, isSelf, registeredState, settings, null, isReleaseChannel, false, null, Optional.empty());
}
public static @NonNull RecipientDetails forDistributionList(String title, @Nullable List<RecipientId> members, @NonNull RecipientRecord record) {
return new RecipientDetails(title, null, Optional.empty(), false, false, record.getRegistered(), record, members, false, false, null, Optional.empty());
}
public static @NonNull RecipientDetails forCallLink(String name, @NonNull RecipientRecord record, @NonNull AvatarColor avatarColor) {
return new RecipientDetails(name, null, Optional.empty(), false, false, record.getRegistered(), record, Collections.emptyList(), false, false, avatarColor, Optional.empty());
}
public static @NonNull RecipientDetails forUnknown() {
return new RecipientDetails();
}
}

View File

@@ -0,0 +1,278 @@
package org.thoughtcrime.securesms.recipients
import android.content.Context
import android.net.Uri
import androidx.annotation.VisibleForTesting
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
import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting
import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState
import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode
import org.thoughtcrime.securesms.database.RecipientTable.VibrateState
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails
import org.thoughtcrime.securesms.database.model.RecipientRecord
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.profiles.ProfileName
import org.thoughtcrime.securesms.recipients.Recipient.HiddenState
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import org.whispersystems.signalservice.api.push.ServiceId.PNI
import java.util.LinkedList
import java.util.Optional
class RecipientDetails private constructor(
@JvmField val aci: ACI?,
@JvmField val pni: PNI?,
@JvmField val username: String?,
@JvmField val e164: String?,
@JvmField val email: String?,
@JvmField val groupId: GroupId?,
@JvmField val distributionListId: DistributionListId?,
/** Used for groups, dlists, and call links */
@JvmField val groupName: String?,
@JvmField val systemContactName: String?,
@JvmField val customLabel: String?,
@JvmField val systemContactPhoto: Uri?,
@JvmField val contactUri: Uri?,
@JvmField val groupAvatarId: Optional<Long>,
@JvmField val messageRingtone: Uri?,
@JvmField val callRingtone: Uri?,
@JvmField val mutedUntil: Long,
@JvmField val messageVibrateState: VibrateState,
@JvmField val callVibrateState: VibrateState,
@JvmField val blocked: Boolean,
@JvmField val expireMessages: Int,
@JvmField val participantIds: List<RecipientId>,
@JvmField val profileName: ProfileName,
@JvmField val registered: RegisteredState,
@JvmField val profileKey: ByteArray?,
@JvmField val expiringProfileKeyCredential: ExpiringProfileKeyCredential?,
@JvmField val profileAvatar: String?,
@JvmField val profileAvatarFileDetails: ProfileAvatarFileDetails,
@JvmField val profileSharing: Boolean,
@JvmField val hiddenState: HiddenState,
@JvmField val isActiveGroup: Boolean,
@JvmField val lastProfileFetch: Long,
@JvmField val isSelf: Boolean,
@JvmField val notificationChannel: String?,
@JvmField val unidentifiedAccessMode: UnidentifiedAccessMode,
@JvmField val capabilities: RecipientRecord.Capabilities,
@JvmField val storageId: ByteArray?,
@JvmField val mentionSetting: MentionSetting,
@JvmField val wallpaper: ChatWallpaper?,
@JvmField val chatColors: ChatColors?,
@JvmField val avatarColor: AvatarColor,
@JvmField val about: String?,
@JvmField val aboutEmoji: String?,
@JvmField val systemProfileName: ProfileName,
@JvmField val extras: Optional<Recipient.Extras>,
@JvmField val hasGroupsInCommon: Boolean,
@JvmField val badges: List<Badge>,
@JvmField val isReleaseChannel: Boolean,
@JvmField val needsPniSignature: Boolean,
@JvmField val callLinkRoomId: CallLinkRoomId?,
@JvmField val groupRecord: Optional<GroupRecord>
) {
@VisibleForTesting
constructor(
groupName: String?,
systemContactName: String?,
isSelf: Boolean,
registeredState: RegisteredState,
record: RecipientRecord,
participantIds: List<RecipientId>?,
isReleaseChannel: Boolean,
avatarColor: AvatarColor?,
groupRecord: Optional<GroupRecord>
) : this(
groupAvatarId = groupRecord.map { if (it.hasAvatar()) it.avatarId else null },
systemContactPhoto = Util.uri(record.systemContactPhotoUri),
customLabel = record.systemPhoneLabel,
contactUri = Util.uri(record.systemContactUri),
aci = record.aci,
pni = record.pni,
username = record.username,
e164 = record.e164,
email = record.email,
groupId = record.groupId,
distributionListId = record.distributionListId,
messageRingtone = record.messageRingtone,
callRingtone = record.callRingtone,
mutedUntil = record.muteUntil,
messageVibrateState = record.messageVibrateState,
callVibrateState = record.callVibrateState,
blocked = record.isBlocked,
expireMessages = record.expireMessages,
participantIds = participantIds ?: LinkedList(),
isActiveGroup = groupRecord.map { it.isActive }.orElse(false),
profileName = record.signalProfileName,
registered = registeredState,
profileKey = record.profileKey,
expiringProfileKeyCredential = record.expiringProfileKeyCredential,
profileAvatar = record.signalProfileAvatar,
profileAvatarFileDetails = record.profileAvatarFileDetails,
profileSharing = record.profileSharing,
hiddenState = record.hiddenState,
lastProfileFetch = record.lastProfileFetch,
isSelf = isSelf,
notificationChannel = record.notificationChannel,
unidentifiedAccessMode = record.unidentifiedAccessMode,
capabilities = record.capabilities,
storageId = record.storageId,
mentionSetting = record.mentionSetting,
wallpaper = record.wallpaper,
chatColors = record.chatColors,
avatarColor = avatarColor ?: record.avatarColor,
about = record.about,
aboutEmoji = record.aboutEmoji,
systemProfileName = record.systemProfileName,
groupName = groupName,
systemContactName = systemContactName,
extras = Optional.ofNullable(record.extras),
hasGroupsInCommon = record.hasGroupsInCommon,
badges = record.badges,
isReleaseChannel = isReleaseChannel,
needsPniSignature = record.needsPniSignature,
callLinkRoomId = record.callLinkRoomId,
groupRecord = groupRecord
)
companion object {
@JvmStatic
fun forIndividual(context: Context, record: RecipientRecord): RecipientDetails {
val isSelf = record.e164 != null && record.e164 == SignalStore.account().e164 || record.aci != null && record.aci == SignalStore.account().aci
val isReleaseChannel = record.id == SignalStore.releaseChannelValues().releaseChannelRecipientId
var registeredState = record.registered
if (isSelf) {
registeredState = if (SignalStore.account().isRegistered && !TextSecurePreferences.isUnauthorizedReceived(context)) {
RegisteredState.REGISTERED
} else {
RegisteredState.NOT_REGISTERED
}
}
return RecipientDetails(
groupName = null,
systemContactName = record.systemDisplayName,
isSelf = isSelf,
registeredState = registeredState,
record = record,
participantIds = null,
isReleaseChannel = isReleaseChannel,
avatarColor = null,
groupRecord = Optional.empty()
)
}
@JvmStatic
fun forGroup(groupRecord: GroupRecord, recipientRecord: RecipientRecord): RecipientDetails {
return RecipientDetails(
groupName = groupRecord.title,
systemContactName = null,
isSelf = false,
registeredState = recipientRecord.registered,
record = recipientRecord,
participantIds = groupRecord.members,
isReleaseChannel = false,
avatarColor = null,
groupRecord = Optional.of(groupRecord)
)
}
@JvmStatic
fun forDistributionList(title: String?, members: List<RecipientId>?, record: RecipientRecord): RecipientDetails {
return RecipientDetails(
groupName = title,
systemContactName = null,
isSelf = false,
registeredState = record.registered,
record = record,
participantIds = members,
isReleaseChannel = false,
avatarColor = null,
groupRecord = Optional.empty()
)
}
@JvmStatic
fun forCallLink(name: String?, record: RecipientRecord, avatarColor: AvatarColor): RecipientDetails {
return RecipientDetails(
groupName = name,
systemContactName = null,
isSelf = false,
registeredState = record.registered,
record = record,
participantIds = emptyList(),
isReleaseChannel = false,
avatarColor = avatarColor,
groupRecord = Optional.empty()
)
}
@JvmStatic
fun forUnknown(): RecipientDetails {
return RecipientDetails(
groupAvatarId = Optional.empty(),
systemContactPhoto = null,
customLabel = null,
contactUri = null,
aci = null,
pni = null,
username = null,
e164 = null,
email = null,
groupId = null,
distributionListId = null,
messageRingtone = null,
callRingtone = null,
mutedUntil = 0,
messageVibrateState = VibrateState.DEFAULT,
callVibrateState = VibrateState.DEFAULT,
blocked = false,
expireMessages = 0,
participantIds = LinkedList(),
profileName = ProfileName.EMPTY,
registered = RegisteredState.UNKNOWN,
profileKey = null,
expiringProfileKeyCredential = null,
profileAvatar = null,
profileAvatarFileDetails = ProfileAvatarFileDetails.NO_DETAILS,
profileSharing = false,
hiddenState = HiddenState.NOT_HIDDEN,
lastProfileFetch = 0,
isSelf = false,
notificationChannel = null,
unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN,
groupName = null,
capabilities = RecipientRecord.Capabilities.UNKNOWN,
storageId = null,
mentionSetting = MentionSetting.ALWAYS_NOTIFY,
wallpaper = null,
chatColors = null,
avatarColor = AvatarColor.UNKNOWN,
about = null,
aboutEmoji = null,
systemProfileName = ProfileName.EMPTY,
systemContactName = null,
extras = Optional.empty(),
hasGroupsInCommon = false,
badges = emptyList(),
isReleaseChannel = false,
needsPniSignature = false,
isActiveGroup = false,
callLinkRoomId = null,
groupRecord = Optional.empty()
)
}
}
}