Convert GroupTable to kotlin.

Also required converting some tests to mockk.
This commit is contained in:
Greyson Parrelli
2023-01-01 23:05:00 -05:00
parent fecfd7cd78
commit 92b9fda6c7
55 changed files with 1756 additions and 1881 deletions
@@ -11,9 +11,9 @@ import org.signal.core.util.logging.Log
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.MediaTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.IdentityRecord
import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
@@ -70,7 +70,7 @@ class ConversationSettingsRepository(
fun isInternalRecipientDetailsEnabled(): Boolean = SignalStore.internalValues().recipientDetails()
fun hasGroups(consumer: (Boolean) -> Unit) {
SignalExecutors.BOUNDED.execute { consumer(SignalDatabase.groups.activeGroupCount > 0) }
SignalExecutors.BOUNDED.execute { consumer(SignalDatabase.groups.getActiveGroupCount() > 0) }
}
fun getIdentity(recipientId: RecipientId, consumer: (IdentityRecord?) -> Unit) {
@@ -91,7 +91,7 @@ class ConversationSettingsRepository(
.getPushGroupsContainingMember(recipientId)
.asSequence()
.filter { it.members.contains(Recipient.self().id) }
.map(GroupTable.GroupRecord::getRecipientId)
.map(GroupRecord::recipientId)
.map(Recipient::resolved)
.sortedBy { gr -> gr.getDisplayName(context) }
.toList()
@@ -129,7 +129,7 @@ class ConversationSettingsRepository(
fun getGroupCapacity(groupId: GroupId, consumer: (GroupCapacityResult) -> Unit) {
SignalExecutors.BOUNDED.execute {
val groupRecord: GroupTable.GroupRecord = SignalDatabase.groups.getGroup(groupId).get()
val groupRecord: GroupRecord = SignalDatabase.groups.getGroup(groupId).get()
consumer(
if (groupRecord.isV2Group) {
val decryptedGroup: DecryptedGroup = groupRecord.requireV2GroupProperties().decryptedGroup
@@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -221,11 +222,11 @@ public class ContactsCursorLoader extends AbstractContactsCursorLoader {
}
private Cursor getGroupsCursor() {
MatrixCursor groupContacts = ContactsCursorRows.createMatrixCursor();
Map<RecipientId, GroupTable.GroupRecord> groups = new LinkedHashMap<>();
MatrixCursor groupContacts = ContactsCursorRows.createMatrixCursor();
Map<RecipientId, GroupRecord> groups = new LinkedHashMap<>();
try (GroupTable.Reader reader = SignalDatabase.groups().queryGroupsByTitle(getFilter(), flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode), !smsEnabled(mode))) {
GroupTable.GroupRecord groupRecord;
GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
groups.put(groupRecord.getRecipientId(), groupRecord);
}
@@ -240,14 +241,14 @@ public class ContactsCursorLoader extends AbstractContactsCursorLoader {
}
try (GroupTable.Reader reader = SignalDatabase.groups().queryGroupsByMembership(filteredContacts, flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode), !smsEnabled(mode))) {
GroupTable.GroupRecord groupRecord;
GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
groups.put(groupRecord.getRecipientId(), groupRecord);
}
}
}
for (GroupTable.GroupRecord groupRecord : groups.values()) {
for (GroupRecord groupRecord : groups.values()) {
groupContacts.addRow(ContactsCursorRows.forGroup(groupRecord));
}
@@ -8,7 +8,7 @@ import android.provider.ContactsContract;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.util.OptionalUtil;
@@ -74,7 +74,7 @@ public final class ContactsCursorRows {
/**
* Create a row for a contacts cursor based off the given group record.
*/
public static @NonNull Object[] forGroup(@NonNull GroupTable.GroupRecord groupRecord) {
public static @NonNull Object[] forGroup(@NonNull GroupRecord groupRecord) {
return new Object[]{groupRecord.getRecipientId().serialize(),
groupRecord.getTitle(),
groupRecord.getId(),
@@ -10,6 +10,7 @@ import androidx.annotation.Nullable;
import org.signal.core.util.Conversions;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
@@ -30,8 +31,8 @@ public final class GroupRecordContactPhoto implements ContactPhoto {
@Override
public InputStream openInputStream(Context context) throws IOException {
GroupTable groupDatabase = SignalDatabase.groups();
Optional<GroupTable.GroupRecord> groupRecord = groupDatabase.getGroup(groupId);
GroupTable groupDatabase = SignalDatabase.groups();
Optional<GroupRecord> groupRecord = groupDatabase.getGroup(groupId);
if (!groupRecord.isPresent() || !AvatarHelper.hasAvatar(context, groupRecord.get().getRecipientId())) {
throw new IOException("No avatar for group: " + groupId);
@@ -6,8 +6,8 @@ import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchCollec
import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchIterator
import org.thoughtcrime.securesms.contacts.paged.collections.CursorSearchIterator
import org.thoughtcrime.securesms.contacts.paged.collections.StoriesSearchCollection
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.StorySend
import org.thoughtcrime.securesms.recipients.Recipient
@@ -8,10 +8,10 @@ import org.thoughtcrime.securesms.contacts.ContactRepository
import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchIterator
import org.thoughtcrime.securesms.database.DistributionListTables
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadTable
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.StorySend
import org.thoughtcrime.securesms.recipients.Recipient
@@ -104,7 +104,7 @@ open class ContactSearchPagedDataSourceRepository(
}
open fun getGroupStories(): Set<ContactSearchData.Story> {
return SignalDatabase.groups.groupsToDisplayAsStories.map {
return SignalDatabase.groups.getGroupsToDisplayAsStories().map {
val recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromGroupId(it))
ContactSearchData.Story(recipient, recipient.participantIds.size, DistributionListPrivacyMode.ALL)
}.toSet()
@@ -16,7 +16,7 @@ import com.annimon.stream.Stream;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
@@ -328,7 +328,8 @@ import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.disposables.Disposable;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
/**
* Fragment for displaying a message thread, as well as
@@ -10,11 +10,11 @@ import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -84,7 +84,7 @@ class ConversationRepository {
boolean isGroup = false;
boolean recipientIsKnownOrHasGroupsInCommon = false;
if (conversationRecipient.isGroup()) {
Optional<GroupTable.GroupRecord> group = SignalDatabase.groups().getGroup(conversationRecipient.getId());
Optional<GroupRecord> group = SignalDatabase.groups().getGroup(conversationRecipient.getId());
if (group.isPresent()) {
List<Recipient> recipients = Recipient.resolvedList(group.get().getMembers());
for (Recipient recipient : recipients) {
@@ -15,8 +15,8 @@ import androidx.fragment.app.FragmentManager;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.ParcelableGroupId;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
@@ -94,7 +94,7 @@ public final class ShowAdminsBottomSheetDialog extends BottomSheetDialogFragment
private static @NonNull List<Recipient> getAdmins(@NonNull Context context, @NonNull GroupId groupId) {
return SignalDatabase.groups()
.getGroup(groupId)
.map(GroupTable.GroupRecord::getAdmins)
.map(GroupRecord::getAdmins)
.orElse(Collections.emptyList());
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.documents.NetworkFailureSet;
import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageExportStatus;
@@ -941,9 +942,9 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
}
public void insertProfileNameChangeMessages(@NonNull Recipient recipient, @NonNull String newProfileName, @NonNull String previousProfileName) {
ThreadTable threadTable = SignalDatabase.threads();
List<GroupTable.GroupRecord> groupRecords = SignalDatabase.groups().getGroupsContainingMember(recipient.getId(), false);
List<Long> threadIdsToUpdate = new LinkedList<>();
ThreadTable threadTable = SignalDatabase.threads();
List<GroupRecord> groupRecords = SignalDatabase.groups().getGroupsContainingMember(recipient.getId(), false);
List<Long> threadIdsToUpdate = new LinkedList<>();
byte[] profileChangeDetails = ProfileChangeDetails.newBuilder()
.setProfileNameChange(ProfileChangeDetails.StringChange.newBuilder()
@@ -959,7 +960,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
try {
threadIdsToUpdate.add(threadTable.getThreadIdFor(recipient.getId()));
for (GroupTable.GroupRecord groupRecord : groupRecords) {
for (GroupRecord groupRecord : groupRecords) {
if (groupRecord.isActive()) {
threadIdsToUpdate.add(threadTable.getThreadIdFor(groupRecord.getRecipientId()));
}
@@ -1032,16 +1033,16 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
}
public void insertNumberChangeMessages(@NonNull RecipientId recipientId) {
ThreadTable threadTable = SignalDatabase.threads();
List<GroupTable.GroupRecord> groupRecords = SignalDatabase.groups().getGroupsContainingMember(recipientId, false);
List<Long> threadIdsToUpdate = new LinkedList<>();
ThreadTable threadTable = SignalDatabase.threads();
List<GroupRecord> groupRecords = SignalDatabase.groups().getGroupsContainingMember(recipientId, false);
List<Long> threadIdsToUpdate = new LinkedList<>();
SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
db.beginTransaction();
try {
threadIdsToUpdate.add(threadTable.getThreadIdFor(recipientId));
for (GroupTable.GroupRecord groupRecord : groupRecords) {
for (GroupRecord groupRecord : groupRecords) {
if (groupRecord.isActive()) {
threadIdsToUpdate.add(threadTable.getThreadIdFor(groupRecord.getRecipientId()));
}
@@ -1186,7 +1186,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
}
}
for (id in groups.allGroupV2Ids) {
for (id in groups.getAllGroupV2Ids()) {
val recipient = Recipient.externalGroupExact(id!!)
val recipientId = recipient.id
val existing: RecipientRecord = getRecordForSync(recipientId) ?: throw AssertionError()
@@ -1670,7 +1670,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val recipientSettings = recipients.getRecord(context, cursor, RECIPIENT_ID)
val recipient: Recipient = if (recipientSettings.groupId != null) {
GroupTable.Reader(cursor).current?.let { group ->
GroupTable.Reader(cursor).getCurrent()?.let { group ->
val details = RecipientDetails(
group.title,
null,
@@ -0,0 +1,163 @@
package org.thoughtcrime.securesms.database.model
import androidx.annotation.WorkerThread
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.signal.storageservice.protos.groups.AccessControl
import org.signal.storageservice.protos.groups.local.EnabledState
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.groups.GroupAccessControl
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil
import org.whispersystems.signalservice.api.push.DistributionId
import java.lang.AssertionError
import java.util.Optional
class GroupRecord(
val id: GroupId,
val recipientId: RecipientId,
val title: String?,
serializedMembers: String?,
serializedUnmigratedV1Members: String?,
val avatarId: Long,
val avatarKey: ByteArray?,
val avatarContentType: String?,
val relay: String?,
val isActive: Boolean,
val avatarDigest: ByteArray?,
val isMms: Boolean,
groupMasterKeyBytes: ByteArray?,
groupRevision: Int,
decryptedGroupBytes: ByteArray?,
val distributionId: DistributionId?,
val lastForceUpdateTimestamp: Long
) {
val members: List<RecipientId> by lazy {
if (serializedMembers.isNullOrEmpty()) {
emptyList()
} else {
RecipientId.fromSerializedList(serializedMembers)
}
}
/** V1 members that were lost during the V1->V2 migration */
val unmigratedV1Members: List<RecipientId> by lazy {
if (serializedUnmigratedV1Members.isNullOrEmpty()) {
emptyList()
} else {
RecipientId.fromSerializedList(serializedUnmigratedV1Members)
}
}
private val v2GroupProperties: GroupTable.V2GroupProperties? by lazy {
if (groupMasterKeyBytes != null && decryptedGroupBytes != null) {
val groupMasterKey = GroupMasterKey(groupMasterKeyBytes)
GroupTable.V2GroupProperties(groupMasterKey, groupRevision, decryptedGroupBytes)
} else {
null
}
}
val description: String
get() = v2GroupProperties?.decryptedGroup?.description ?: ""
val isAnnouncementGroup: Boolean
get() = v2GroupProperties?.decryptedGroup?.isAnnouncementGroup == EnabledState.ENABLED
val isV1Group: Boolean
get() = !isMms && !isV2Group
val isV2Group: Boolean
get() = v2GroupProperties != null
@get:WorkerThread
val admins: List<Recipient>
get() {
return if (v2GroupProperties != null) {
val resolved = members.map { Recipient.resolved(it) }
v2GroupProperties!!.getAdmins(resolved)
} else {
emptyList()
}
}
/** Who is allowed to add to the membership of this group. */
val membershipAdditionAccessControl: GroupAccessControl
get() {
return if (isV2Group) {
if (requireV2GroupProperties().decryptedGroup.accessControl.members == AccessControl.AccessRequired.MEMBER) {
GroupAccessControl.ALL_MEMBERS
} else {
GroupAccessControl.ONLY_ADMINS
}
} else if (isV1Group) {
GroupAccessControl.NO_ONE
} else if (id.isV1) {
GroupAccessControl.ALL_MEMBERS
} else {
GroupAccessControl.ONLY_ADMINS
}
}
/** Who is allowed to modify the attributes of this group, name/avatar/timer etc. */
val attributesAccessControl: GroupAccessControl
get() {
return if (isV2Group) {
if (requireV2GroupProperties().decryptedGroup.accessControl.attributes == AccessControl.AccessRequired.MEMBER) {
GroupAccessControl.ALL_MEMBERS
} else {
GroupAccessControl.ONLY_ADMINS
}
} else if (isV1Group) {
GroupAccessControl.NO_ONE
} else {
GroupAccessControl.ALL_MEMBERS
}
}
fun hasAvatar(): Boolean {
return avatarId != 0L
}
fun requireV2GroupProperties(): GroupTable.V2GroupProperties {
return v2GroupProperties ?: throw AssertionError()
}
fun isAdmin(recipient: Recipient): Boolean {
return isV2Group && requireV2GroupProperties().isAdmin(recipient)
}
fun memberLevel(recipient: Recipient): GroupTable.MemberLevel {
return if (isV2Group) {
val memberLevel = requireV2GroupProperties().memberLevel(recipient.serviceId)
if (recipient.isSelf && memberLevel == GroupTable.MemberLevel.NOT_A_MEMBER) {
requireV2GroupProperties().memberLevel(Optional.ofNullable(SignalStore.account().pni))
} else {
memberLevel
}
} else if (isMms && recipient.isSelf) {
GroupTable.MemberLevel.FULL_MEMBER
} else if (members.contains(recipient.id)) {
GroupTable.MemberLevel.FULL_MEMBER
} else {
GroupTable.MemberLevel.NOT_A_MEMBER
}
}
/**
* Whether or not the recipient is a pending member.
*/
fun isPendingMember(recipient: Recipient): Boolean {
if (isV2Group) {
val serviceId = recipient.serviceId
if (serviceId.isPresent) {
return DecryptedGroupUtil.findPendingByUuid(requireV2GroupProperties().decryptedGroup.pendingMembersList, serviceId.get().uuid())
.isPresent
}
}
return false
}
}
@@ -10,6 +10,7 @@ import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -78,7 +79,7 @@ class DeleteAccountRepository {
int groupsLeft = 0;
try (GroupTable.Reader groups = SignalDatabase.groups().getGroups()) {
GroupTable.GroupRecord groupRecord = groups.getNext();
GroupRecord groupRecord = groups.getNext();
onDeleteAccountEvent.accept(new DeleteAccountEvent.LeaveGroupsProgress(groups.getCount(), 0));
Log.i(TAG, "deleteAccount: found " + groups.getCount() + " groups to leave.");
@@ -15,6 +15,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
@@ -402,14 +403,14 @@ public final class GroupManager {
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, GroupChangeBusyException, MembershipNotSuitableForV2Exception
{
if (groupId.isV2()) {
GroupTable.GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
return editor.addMembers(newMembers, groupRecord.requireV2GroupProperties().getBannedMembers());
}
} else {
GroupTable.GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
List<RecipientId> members = groupRecord.getMembers();
GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
List<RecipientId> members = groupRecord.getMembers();
byte[] avatar = groupRecord.hasAvatar() ? AvatarHelper.getAvatarBytes(context, groupRecord.getRecipientId()) : null;
Set<RecipientId> recipientIds = new HashSet<>(members);
int originalSize = recipientIds.size();
@@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper;
@@ -167,8 +168,8 @@ final class GroupManagerV2 {
@WorkerThread
@NonNull Map<UUID, UuidCiphertext> getUuidCipherTexts(@NonNull GroupId.V2 groupId) {
GroupTable.GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
GroupTable.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
GroupTable.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
GroupMasterKey groupMasterKey = v2GroupProperties.getGroupMasterKey();
ClientZkGroupCipher clientZkGroupCipher = new ClientZkGroupCipher(GroupSecretParams.deriveFromMasterKey(groupMasterKey));
List<Recipient> recipients = v2GroupProperties.getMemberRecipients(GroupTable.MemberSet.FULL_MEMBERS_INCLUDING_SELF);
@@ -251,9 +252,9 @@ final class GroupManagerV2 {
throws IOException, MembershipNotSuitableForV2Exception, GroupAlreadyExistsException, GroupChangeFailedException
{
GroupMasterKey groupMasterKey = groupIdV1.deriveV2MigrationMasterKey();
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
GroupTable.GroupRecord groupRecord = groupDatabase.requireGroup(groupIdV1);
String name = Util.emptyIfNull(groupRecord.getTitle());
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
GroupRecord groupRecord = groupDatabase.requireGroup(groupIdV1);
String name = Util.emptyIfNull(groupRecord.getTitle());
byte[] avatar = groupRecord.hasAvatar() ? AvatarHelper.getAvatarBytes(context, groupRecord.getRecipientId()) : null;
int messageTimer = Recipient.resolved(groupRecord.getRecipientId()).getExpiresInSeconds();
Set<RecipientId> memberIds = Stream.of(members)
@@ -327,7 +328,7 @@ final class GroupManagerV2 {
GroupEditor(@NonNull GroupId.V2 groupId, @NonNull Closeable lock) {
super(lock);
GroupTable.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
this.groupId = groupId;
this.v2GroupProperties = groupRecord.requireV2GroupProperties();
@@ -455,8 +456,8 @@ final class GroupManagerV2 {
void leaveGroup()
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
{
GroupTable.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
DecryptedGroup decryptedGroup = groupRecord.requireV2GroupProperties().getDecryptedGroup();
GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
DecryptedGroup decryptedGroup = groupRecord.requireV2GroupProperties().getDecryptedGroup();
Optional<DecryptedMember> selfMember = DecryptedGroupUtil.findMemberByUuid(decryptedGroup.getMembersList(), selfAci.uuid());
Optional<DecryptedPendingMember> aciPendingMember = DecryptedGroupUtil.findPendingByUuid(decryptedGroup.getPendingMembersList(), selfAci.uuid());
Optional<DecryptedPendingMember> pniPendingMember = DecryptedGroupUtil.findPendingByUuid(decryptedGroup.getPendingMembersList(), selfPni.uuid());
@@ -728,7 +729,7 @@ final class GroupManagerV2 {
private GroupManager.GroupActionResult commitChange(@NonNull GroupChange.Actions.Builder change, boolean allowWhenBlocked, boolean sendToMembers)
throws GroupNotAMemberException, GroupChangeFailedException, IOException, GroupInsufficientRightsException
{
final GroupTable.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
final GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
final GroupTable.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
final int nextRevision = v2GroupProperties.getGroupRevision() + 1;
final GroupChange.Actions changeActions = change.setRevision(nextRevision).build();
@@ -923,7 +924,7 @@ final class GroupManagerV2 {
alreadyAMember = true;
}
Optional<GroupTable.GroupRecord> unmigratedV1Group = groupDatabase.getGroupV1ByExpectedV2(groupId);
Optional<GroupRecord> unmigratedV1Group = groupDatabase.getGroupV1ByExpectedV2(groupId);
if (unmigratedV1Group.isPresent()) {
Log.i(TAG, "Group link was for a migrated V1 group we know about! Migrating it and using that as the base.");
@@ -932,7 +933,7 @@ final class GroupManagerV2 {
DecryptedGroup decryptedGroup = createPlaceholderGroup(joinInfo, requestToJoin);
Optional<GroupTable.GroupRecord> group = groupDatabase.getGroup(groupId);
Optional<GroupRecord> group = groupDatabase.getGroup(groupId);
if (group.isPresent()) {
Log.i(TAG, "Group already present locally");
@@ -19,6 +19,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
@@ -48,7 +49,7 @@ public final class LiveGroup {
private final GroupTable groupDatabase;
private final LiveData<Recipient> recipient;
private final LiveData<GroupTable.GroupRecord> groupRecord;
private final LiveData<GroupRecord> groupRecord;
private final LiveData<List<GroupMemberEntry.FullMember>> fullMembers;
private final LiveData<List<GroupMemberEntry.RequestingMember>> requestingMembers;
private final LiveData<GroupLinkUrlAndStatus> groupLink;
@@ -64,7 +65,7 @@ public final class LiveGroup {
this.requestingMembers = mapToRequestingMembers(this.groupRecord);
if (groupId.isV2()) {
LiveData<GroupTable.V2GroupProperties> v2Properties = Transformations.map(this.groupRecord, GroupTable.GroupRecord::requireV2GroupProperties);
LiveData<GroupTable.V2GroupProperties> v2Properties = Transformations.map(this.groupRecord, GroupRecord::requireV2GroupProperties);
this.groupLink = Transformations.map(v2Properties, g -> {
DecryptedGroup group = g.getDecryptedGroup();
AccessControl.AccessRequired addFromInviteLink = group.getAccessControl().getAddFromInviteLink();
@@ -87,7 +88,7 @@ public final class LiveGroup {
SignalExecutors.BOUNDED.execute(() -> liveRecipient.postValue(Recipient.externalGroupExact(groupId).live()));
}
protected static LiveData<List<GroupMemberEntry.FullMember>> mapToFullMembers(@NonNull LiveData<GroupTable.GroupRecord> groupRecord) {
protected static LiveData<List<GroupMemberEntry.FullMember>> mapToFullMembers(@NonNull LiveData<GroupRecord> groupRecord) {
return LiveDataUtil.mapAsync(groupRecord,
g -> Stream.of(g.getMembers())
.map(m -> {
@@ -98,7 +99,7 @@ public final class LiveGroup {
.toList());
}
protected static LiveData<List<GroupMemberEntry.RequestingMember>> mapToRequestingMembers(@NonNull LiveData<GroupTable.GroupRecord> groupRecord) {
protected static LiveData<List<GroupMemberEntry.RequestingMember>> mapToRequestingMembers(@NonNull LiveData<GroupRecord> groupRecord) {
return LiveDataUtil.mapAsync(groupRecord,
g -> {
if (!g.isV2Group()) {
@@ -128,11 +129,11 @@ public final class LiveGroup {
}
public LiveData<String> getDescription() {
return Transformations.map(groupRecord, GroupTable.GroupRecord::getDescription);
return Transformations.map(groupRecord, GroupRecord::getDescription);
}
public LiveData<Boolean> isAnnouncementGroup() {
return Transformations.map(groupRecord, GroupTable.GroupRecord::isAnnouncementGroup);
return Transformations.map(groupRecord, GroupRecord::isAnnouncementGroup);
}
public LiveData<Recipient> getGroupRecipient() {
@@ -148,7 +149,7 @@ public final class LiveGroup {
}
public LiveData<Boolean> isActive() {
return Transformations.map(groupRecord, GroupTable.GroupRecord::isActive);
return Transformations.map(groupRecord, GroupRecord::isActive);
}
public LiveData<Boolean> getRecipientIsAdmin(@NonNull RecipientId recipientId) {
@@ -171,11 +172,11 @@ public final class LiveGroup {
}
public LiveData<GroupAccessControl> getMembershipAdditionAccessControl() {
return Transformations.map(groupRecord, GroupTable.GroupRecord::getMembershipAdditionAccessControl);
return Transformations.map(groupRecord, GroupRecord::getMembershipAdditionAccessControl);
}
public LiveData<GroupAccessControl> getAttributesAccessControl() {
return Transformations.map(groupRecord, GroupTable.GroupRecord::getAttributesAccessControl);
return Transformations.map(groupRecord, GroupRecord::getAttributesAccessControl);
}
public LiveData<List<GroupMemberEntry.FullMember>> getNonAdminFullMembers() {
@@ -14,6 +14,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupChangeException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
@@ -56,7 +57,7 @@ public final class LeaveGroupDialog {
SimpleTask.run(activity.getLifecycle(), () -> {
GroupTable.V2GroupProperties groupProperties = SignalDatabase.groups()
.getGroup(groupId)
.map(GroupTable.GroupRecord::requireV2GroupProperties)
.map(GroupRecord::requireV2GroupProperties)
.orElse(null);
if (groupProperties != null && groupProperties.isAdmin(Recipient.self())) {
@@ -20,7 +20,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
@@ -5,7 +5,7 @@ import androidx.annotation.VisibleForTesting;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
@@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.InvalidMessageException;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
@@ -10,7 +10,7 @@ import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
@@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
@@ -28,7 +28,7 @@ public final class ForceUpdateGroupV2Job extends BaseJob {
public static void enqueueIfNecessary(@NonNull GroupId.V2 groupId) {
SignalExecutors.BOUNDED.execute(() -> {
Optional<GroupTable.GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
Optional<GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
if (group.isPresent() &&
group.get().isV2Group() &&
group.get().getLastForceUpdateTimestamp() + FORCE_UPDATE_INTERVAL < System.currentTimeMillis()
@@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
@@ -61,7 +61,7 @@ final class ForceUpdateGroupV2WorkerJob extends BaseJob {
@Override
public void onRun() throws IOException, GroupNotAMemberException, GroupChangeBusyException {
Optional<GroupTable.GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
Optional<GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
if (!group.isPresent()) {
Log.w(TAG, "Group not found");
@@ -8,8 +8,8 @@ import com.google.protobuf.ByteString;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
@@ -112,7 +112,7 @@ public final class GroupV2UpdateSelfProfileKeyJob extends BaseJob {
boolean foundMismatch = false;
for (GroupId.V2 id : SignalDatabase.groups().getAllGroupV2Ids()) {
Optional<GroupTable.GroupRecord> group = SignalDatabase.groups().getGroup(id);
Optional<GroupRecord> group = SignalDatabase.groups().getGroup(id);
if (!group.isPresent()) {
Log.w(TAG, "Group " + group + " no longer exists?");
continue;
@@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
@@ -101,7 +102,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob {
DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]));
boolean hasData = false;
GroupTable.GroupRecord record;
GroupRecord record;
while ((record = reader.getNext()) != null) {
if (record.isV1Group()) {
@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -252,7 +253,7 @@ public final class PushGroupSendJob extends PushSendJob {
.anyMatch(info -> info.getStatus() > GroupReceiptTable.STATUS_UNDELIVERED);
if (message.getStoryType().isStory()) {
Optional<GroupTable.GroupRecord> groupRecord = SignalDatabase.groups().getGroup(groupId);
Optional<GroupRecord> groupRecord = SignalDatabase.groups().getGroup(groupId);
if (groupRecord.isPresent() && groupRecord.get().isAnnouncementGroup() && !groupRecord.get().isAdmin(Recipient.self())) {
throw new UndeliverableMessageException("Non-admins cannot send stories in announcement groups!");
@@ -301,7 +302,7 @@ public final class PushGroupSendJob extends PushSendJob {
throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!");
}
} else {
Optional<GroupTable.GroupRecord> groupRecord = SignalDatabase.groups().getGroup(groupRecipient.requireGroupId());
Optional<GroupRecord> groupRecord = SignalDatabase.groups().getGroup(groupRecipient.requireGroupId());
if (groupRecord.isPresent() && groupRecord.get().isAnnouncementGroup() && !groupRecord.get().isAdmin(Recipient.self())) {
throw new UndeliverableMessageException("Non-admins cannot send messages in announcement groups!");
@@ -4,8 +4,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
@@ -76,7 +76,7 @@ final class RequestGroupV2InfoWorkerJob extends BaseJob {
Log.i(TAG, "Updating group to revision " + toRevision);
}
Optional<GroupTable.GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
Optional<GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
if (!group.isPresent()) {
Log.w(TAG, "Group not found");
@@ -11,7 +11,7 @@ import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.SignalProtocolAddress;
import org.signal.libsignal.protocol.message.SenderKeyDistributionMessage;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -23,8 +23,8 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.database.AttachmentTable;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
@@ -282,14 +282,14 @@ public class LinkPreviewRepository {
}
GroupMasterKey groupMasterKey = groupInviteLinkUrl.getGroupMasterKey();
GroupId.V2 groupId = GroupId.v2(groupMasterKey);
Optional<GroupTable.GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
GroupId.V2 groupId = GroupId.v2(groupMasterKey);
Optional<GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
if (group.isPresent()) {
Log.i(TAG, "Creating preview for locally available group");
GroupTable.GroupRecord groupRecord = group.get();
String title = groupRecord.getTitle();
GroupRecord groupRecord = group.get();
String title = groupRecord.getTitle();
int memberCount = groupRecord.getMembers().size();
String description = getMemberCountDescription(context, memberCount);
Optional<Attachment> thumbnail = Optional.empty();
@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -121,7 +122,7 @@ class CameraContactsRepository {
List<Recipient> recipients = new ArrayList<>();
try (GroupTable.Reader reader = groupDatabase.queryGroupsByTitle(query, false, true, true)) {
GroupTable.GroupRecord groupRecord;
GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
RecipientId recipientId = recipientTable.getOrInsertFromGroupId(groupRecord.getId());
recipients.add(Recipient.resolved(recipientId));
@@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeException;
import org.thoughtcrime.securesms.groups.GroupManager;
@@ -58,8 +59,8 @@ final class MessageRequestRepository {
void getGroupInfo(@NonNull RecipientId recipientId, @NonNull Consumer<GroupInfo> onGroupInfoLoaded) {
executor.execute(() -> {
GroupTable groupDatabase = SignalDatabase.groups();
Optional<GroupTable.GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
GroupTable groupDatabase = SignalDatabase.groups();
Optional<GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
onGroupInfoLoaded.accept(groupRecord.map(record -> {
if (record.isV2Group()) {
DecryptedGroup decryptedGroup = record.requireV2GroupProperties().getDecryptedGroup();
@@ -12,7 +12,7 @@ import org.signal.libsignal.protocol.InvalidRegistrationIdException;
import org.signal.libsignal.protocol.NoSessionException;
import org.thoughtcrime.securesms.crypto.SenderKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.MessageSendLogTables;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListId;
@@ -35,7 +35,7 @@ import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.AttachmentTable;
import org.thoughtcrime.securesms.database.CallTable;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.GroupReceiptTable;
import org.thoughtcrime.securesms.database.GroupReceiptTable.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MessageTable;
@@ -18,9 +18,9 @@ import androidx.core.graphics.drawable.IconCompat
import org.signal.core.util.PendingIntentFlags.mutable
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.conversation.ConversationIntents
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.notifications.ReplyMethod
@@ -117,7 +117,7 @@ sealed class NotificationBuilder(protected val context: Context) {
fun addReplyActions(conversation: NotificationConversation) {
if (privacy.isDisplayMessage && isNotLocked && !conversation.recipient.isPushV1Group && RecipientUtil.isMessageRequestAccepted(context, conversation.recipient)) {
if (conversation.recipient.isPushV2Group) {
val group: Optional<GroupTable.GroupRecord> = SignalDatabase.groups.getGroup(conversation.recipient.requireGroupId())
val group: Optional<GroupRecord> = SignalDatabase.groups.getGroup(conversation.recipient.requireGroupId())
if (group.isPresent && group.get().isAnnouncementGroup && !group.get().isAdmin(Recipient.self())) {
return
}
@@ -10,8 +10,8 @@ import androidx.core.util.Consumer;
import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupChangeException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
@@ -92,7 +92,7 @@ class EditGroupProfileRepository implements EditProfileRepository {
return SignalDatabase.groups()
.getGroup(recipientId)
.map(GroupTable.GroupRecord::getDescription)
.map(GroupRecord::getDescription)
.orElse("");
}, descriptionConsumer::accept);
}
@@ -9,6 +9,7 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -105,7 +106,7 @@ public final class ReviewUtil {
return Stream.of(SignalDatabase.groups()
.getPushGroupsContainingMember(recipientId))
.filter(g -> g.getMembers().contains(Recipient.self().getId()))
.map(GroupTable.GroupRecord::getRecipientId)
.map(GroupRecord::getRecipientId)
.toList()
.size();
}
@@ -15,7 +15,7 @@ import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DistributionListTables;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.GroupTable.GroupRecord;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
@@ -21,6 +21,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
@@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeException;
@@ -301,7 +302,7 @@ public class RecipientUtil {
GroupTable groupDatabase = SignalDatabase.groups();
return groupDatabase.getPushGroupsContainingMember(recipient.getId())
.stream()
.anyMatch(GroupTable.GroupRecord::isV2Group);
.anyMatch(GroupRecord::isV2Group);
}
}
@@ -11,6 +11,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeException;
@@ -106,11 +107,11 @@ final class RecipientDialogRepository {
void getGroupMembership(@NonNull Consumer<List<RecipientId>> onComplete) {
SimpleTask.run(SignalExecutors.UNBOUNDED,
() -> {
GroupTable groupDatabase = SignalDatabase.groups();
List<GroupTable.GroupRecord> groupRecords = groupDatabase.getPushGroupsContainingMember(recipientId);
ArrayList<RecipientId> groupRecipients = new ArrayList<>(groupRecords.size());
GroupTable groupDatabase = SignalDatabase.groups();
List<GroupRecord> groupRecords = groupDatabase.getPushGroupsContainingMember(recipientId);
ArrayList<RecipientId> groupRecipients = new ArrayList<>(groupRecords.size());
for (GroupTable.GroupRecord groupRecord : groupRecords) {
for (GroupRecord groupRecord : groupRecords) {
groupRecipients.add(groupRecord.getRecipientId());
}
@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SearchTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
@@ -174,7 +175,7 @@ public class SearchRepository {
Set<RecipientId> groupsByTitleIds = new LinkedHashSet<>();
GroupTable.GroupRecord record;
GroupRecord record;
try (GroupTable.Reader reader = SignalDatabase.groups().queryGroupsByTitle(query, true, false, false)) {
while ((record = reader.getNext()) != null) {
groupsByTitleIds.add(record.getRecipientId());
@@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.CallTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
@@ -748,9 +749,9 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
@Override
public void onGroupCallRingUpdate(@NonNull byte[] groupIdBytes, long ringId, @NonNull UUID sender, @NonNull CallManager.RingUpdate ringUpdate) {
try {
GroupId.V2 groupId = GroupId.v2(new GroupIdentifier(groupIdBytes));
GroupTable.GroupRecord group = SignalDatabase.groups().getGroup(groupId).orElse(null);
Recipient senderRecipient = Recipient.externalPush(ServiceId.from(sender));
GroupId.V2 groupId = GroupId.v2(new GroupIdentifier(groupIdBytes));
GroupRecord group = SignalDatabase.groups().getGroup(groupId).orElse(null);
Recipient senderRecipient = Recipient.externalPush(ServiceId.from(sender));
if (group != null &&
group.isActive() &&
@@ -9,6 +9,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.BadGroupIdException;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -46,8 +47,8 @@ public final class GroupV1RecordProcessor extends DefaultStorageRecordProcessor<
@Override
boolean isInvalid(@NonNull SignalGroupV1Record remote) {
try {
GroupId.V1 id = GroupId.v1(remote.getGroupId());
Optional<GroupTable.GroupRecord> v2Record = groupDatabase.getGroup(id.deriveV2MigrationGroupId());
GroupId.V1 id = GroupId.v1(remote.getGroupId());
Optional<GroupRecord> v2Record = groupDatabase.getGroup(id.deriveV2MigrationGroupId());
if (v2Record.isPresent()) {
Log.w(TAG, "We already have an upgraded V2 group for this V1 group -- marking as invalid.");
@@ -29,8 +29,8 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.ui.invitesandrequests.joining.GroupJoinBottomSheetDialogFragment;
@@ -237,7 +237,7 @@ public class CommunicationActions {
GroupId.V2 groupId = GroupId.v2(groupInviteLinkUrl.getGroupMasterKey());
SimpleTask.run(SignalExecutors.BOUNDED, () -> {
GroupTable.GroupRecord group = SignalDatabase.groups().getGroup(groupId).orElse(null);
GroupRecord group = SignalDatabase.groups().getGroup(groupId).orElse(null);
return group != null && group.isActive() ? Recipient.resolved(group.getRecipientId())
: null;
@@ -14,6 +14,7 @@ import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -94,7 +95,7 @@ public final class GroupUtil {
{
if (groupId.isV2()) {
GroupTable groupDatabase = SignalDatabase.groups();
GroupTable.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
GroupTable.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
SignalServiceGroupV2 group = SignalServiceGroupV2.newBuilder(v2GroupProperties.getGroupMasterKey())
.withRevision(v2GroupProperties.getGroupRevision())
@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.database.IdentityTable;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.MessageTable.InsertResult;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -67,7 +68,7 @@ public final class IdentityUtil {
try (GroupTable.Reader reader = groupDatabase.getGroups()) {
GroupTable.GroupRecord groupRecord;
GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive() && !groupRecord.isMms()) {
@@ -138,7 +139,7 @@ public final class IdentityUtil {
GroupTable groupDatabase = SignalDatabase.groups();
try (GroupTable.Reader reader = groupDatabase.getGroups()) {
GroupTable.GroupRecord groupRecord;
GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
if (groupRecord.getMembers().contains(recipientId) && groupRecord.isActive()) {
@@ -13,6 +13,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember
import org.signal.storageservice.protos.groups.local.DecryptedString
import org.signal.storageservice.protos.groups.local.DecryptedTimer
import org.signal.storageservice.protos.groups.local.EnabledState
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupHistoryEntry
@@ -99,7 +100,7 @@ class GroupChangeData(private val revision: Int, private val groupOperations: Gr
class GroupStateTestData(private val masterKey: GroupMasterKey, private val groupOperations: GroupsV2Operations.GroupOperations? = null) {
var localState: DecryptedGroup? = null
var groupRecord: Optional<GroupTable.GroupRecord> = Optional.empty()
var groupRecord: Optional<GroupRecord> = Optional.empty()
var serverState: DecryptedGroup? = null
var changeSet: ChangeSet? = null
var groupChange: GroupChange? = null
@@ -172,9 +173,9 @@ fun groupRecord(
avatarDigest: ByteArray = ByteArray(0),
mms: Boolean = false,
distributionId: DistributionId? = null
): Optional<GroupTable.GroupRecord> {
): Optional<GroupRecord> {
return Optional.of(
GroupTable.GroupRecord(
GroupRecord(
id,
recipientId,
decryptedGroup.title,
@@ -4,6 +4,10 @@ package org.thoughtcrime.securesms.groups
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers
import org.hamcrest.Matchers.`is`
@@ -11,9 +15,6 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.signal.core.util.Hex
@@ -71,8 +72,6 @@ class GroupManagerV2Test_edit {
private lateinit var sendGroupUpdateHelper: GroupManagerV2.SendGroupUpdateHelper
private lateinit var groupOperations: GroupsV2Operations.GroupOperations
private lateinit var patchedDecryptedGroup: ArgumentCaptor<DecryptedGroup>
private lateinit var manager: GroupManagerV2
@get:Rule
@@ -87,17 +86,15 @@ class GroupManagerV2Test_edit {
val clientZkOperations = ClientZkOperations(server.getServerPublicParams())
groupTable = mock(GroupTable::class.java)
groupsV2API = mock(GroupsV2Api::class.java)
groupTable = mockk()
groupsV2API = mockk()
groupsV2Operations = GroupsV2Operations(clientZkOperations, 1000)
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
groupsV2StateProcessor = mock(GroupsV2StateProcessor::class.java)
groupCandidateHelper = mock(GroupCandidateHelper::class.java)
sendGroupUpdateHelper = mock(GroupManagerV2.SendGroupUpdateHelper::class.java)
groupsV2Authorization = mockk(relaxed = true)
groupsV2StateProcessor = mockk()
groupCandidateHelper = mockk()
sendGroupUpdateHelper = mockk()
groupOperations = groupsV2Operations.forGroup(groupSecretParams)
patchedDecryptedGroup = ArgumentCaptor.forClass(DecryptedGroup::class.java)
manager = GroupManagerV2(
ApplicationProvider.getApplicationContext(),
groupTable,
@@ -115,12 +112,11 @@ class GroupManagerV2Test_edit {
val data = GroupStateTestData(masterKey, groupOperations)
data.init()
Mockito.doReturn(data.groupRecord).`when`(groupTable).getGroup(groupId)
Mockito.doReturn(data.groupRecord.get()).`when`(groupTable).requireGroup(groupId)
Mockito.doReturn(GroupManagerV2.RecipientAndThread(Recipient.UNKNOWN, 1)).`when`(sendGroupUpdateHelper).sendGroupUpdate(Mockito.eq(masterKey), Mockito.any(), Mockito.any(), Mockito.anyBoolean())
Mockito.doReturn(data.groupChange!!).`when`(groupsV2API).patchGroup(Mockito.any(), Mockito.any(), Mockito.any())
every { groupTable.getGroup(groupId) } returns data.groupRecord
every { groupTable.requireGroup(groupId) } returns data.groupRecord.get()
every { groupTable.update(any<GroupId.V2>(), any()) } returns Unit
every { sendGroupUpdateHelper.sendGroupUpdate(masterKey, any(), any(), any()) } returns GroupManagerV2.RecipientAndThread(Recipient.UNKNOWN, 1)
every { groupsV2API.patchGroup(any(), any(), any()) } returns data.groupChange!!
}
private fun editGroup(perform: GroupManagerV2.GroupEditor.() -> Unit) {
@@ -128,8 +124,9 @@ class GroupManagerV2Test_edit {
}
private fun then(then: (DecryptedGroup) -> Unit) {
Mockito.verify(groupTable).update(Mockito.eq(groupId), patchedDecryptedGroup.capture())
then(patchedDecryptedGroup.value)
val decryptedGroupArg = slot<DecryptedGroup>()
verify { groupTable.update(groupId, capture(decryptedGroupArg)) }
then(decryptedGroupArg.captured)
}
@Test
@@ -2,6 +2,10 @@ package org.thoughtcrime.securesms.groups.v2.processing
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.both
import org.hamcrest.Matchers.hasItem
@@ -13,18 +17,6 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.any
import org.mockito.Mockito.doCallRealMethod
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.isA
import org.mockito.Mockito.mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doNothing
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.signal.core.util.Hex.fromStringCondensed
@@ -48,6 +40,7 @@ import org.thoughtcrime.securesms.database.setNewTitle
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.groups.GroupsV2Authorization
import org.thoughtcrime.securesms.jobmanager.JobManager
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger
import org.thoughtcrime.securesms.testutil.SystemOutLogger
@@ -77,6 +70,7 @@ class GroupsV2StateProcessorTest {
private lateinit var groupsV2API: GroupsV2Api
private lateinit var groupsV2Authorization: GroupsV2Authorization
private lateinit var profileAndMessageHelper: GroupsV2StateProcessor.ProfileAndMessageHelper
private lateinit var jobManager: JobManager
private lateinit var processor: GroupsV2StateProcessor.StateProcessorForGroup
@@ -88,25 +82,29 @@ class GroupsV2StateProcessorTest {
Log.initialize(SystemOutLogger())
SignalProtocolLoggerProvider.setProvider(CustomSignalProtocolLogger())
groupTable = mock(GroupTable::class.java)
recipientTable = mock(RecipientTable::class.java)
groupsV2API = mock(GroupsV2Api::class.java)
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
profileAndMessageHelper = mock(GroupsV2StateProcessor.ProfileAndMessageHelper::class.java)
groupTable = mockk(relaxed = true)
recipientTable = mockk()
groupsV2API = mockk()
groupsV2Authorization = mockk(relaxed = true)
profileAndMessageHelper = mockk(relaxed = true)
jobManager = mockk(relaxed = true)
mockkStatic(ApplicationDependencies::class)
every { ApplicationDependencies.getJobManager() } returns jobManager
processor = GroupsV2StateProcessor.StateProcessorForGroup(serviceIds, ApplicationProvider.getApplicationContext(), groupTable, groupsV2API, groupsV2Authorization, masterKey, profileAndMessageHelper)
}
@After
fun tearDown() {
reset(ApplicationDependencies.getJobManager())
// reset(ApplicationDependencies.getJobManager())
}
private fun given(init: GroupStateTestData.() -> Unit) {
val data = givenData(init)
doReturn(data.groupRecord).`when`(groupTable).getGroup(any(GroupId.V2::class.java))
doReturn(!data.groupRecord.isPresent).`when`(groupTable).isUnknownGroup(any())
every { groupTable.getGroup(any<GroupId.V2>()) } returns data.groupRecord
every { groupTable.isUnknownGroup(any()) } returns !data.groupRecord.isPresent
data.serverState?.let { serverState ->
val testPartial = object : PartialDecryptedGroup(null, serverState, null, null) {
@@ -114,12 +112,13 @@ class GroupsV2StateProcessorTest {
return serverState
}
}
doReturn(testPartial).`when`(groupsV2API).getPartialDecryptedGroup(any(), any())
doReturn(serverState).`when`(groupsV2API).getGroup(any(), any())
every { groupsV2API.getPartialDecryptedGroup(any(), any()) } returns testPartial
every { groupsV2API.getGroup(any(), any()) } returns serverState
}
data.changeSet?.let { changeSet ->
doReturn(changeSet.toApiResponse()).`when`(groupsV2API).getGroupHistoryPage(any(), eq(data.requestedRevision), any(), eq(data.includeFirst))
every { groupsV2API.getGroupHistoryPage(any(), data.requestedRevision, any(), data.includeFirst) } returns changeSet.toApiResponse()
}
}
@@ -306,15 +305,14 @@ class GroupsV2StateProcessorTest {
apiCallParameters(2, true)
}
doReturn(true).`when`(groupTable).isUnknownGroup(any())
every { groupTable.isUnknownGroup(any()) } returns true
val result = processor.updateLocalGroupToRevision(2, 0, DecryptedGroupChange.getDefaultInstance())
assertThat("local should update to revision added", result.groupState, `is`(GroupsV2StateProcessor.GroupState.GROUP_UPDATED))
assertThat("revision matches peer revision added", result.latestServer!!.revision, `is`(2))
assertThat("title matches that as it was in revision added", result.latestServer!!.title, `is`("Baking Signal for Science"))
verify(ApplicationDependencies.getJobManager()).add(isA(RequestGroupV2InfoJob::class.java))
verify { jobManager.add(ofType(RequestGroupV2InfoJob::class)) }
}
@Test
@@ -406,7 +404,7 @@ class GroupsV2StateProcessorTest {
assertThat("local should update to server", result.groupState, `is`(GroupsV2StateProcessor.GroupState.GROUP_UPDATED))
assertThat("revision matches revision approved at", result.latestServer!!.revision, `is`(3))
assertThat("title matches revision approved at", result.latestServer!!.title, `is`("Beam me up"))
verify(ApplicationDependencies.getJobManager()).add(isA(RequestGroupV2InfoJob::class.java))
verify { jobManager.add(ofType(RequestGroupV2InfoJob::class)) }
}
@Test
@@ -458,7 +456,7 @@ class GroupsV2StateProcessorTest {
}
}
}
doReturn(secondApiCallChangeSet.changeSet!!.toApiResponse()).`when`(groupsV2API).getGroupHistoryPage(any(), eq(100), any(), eq(true))
every { groupsV2API.getGroupHistoryPage(any(), 100, any(), true) } returns secondApiCallChangeSet.changeSet!!.toApiResponse()
val result = processor.updateLocalGroupToRevision(GroupsV2StateProcessor.LATEST, 0, null)
@@ -474,10 +472,11 @@ class GroupsV2StateProcessorTest {
fun missedMemberAddResolvesWithMultipleRevisionUpdate() {
val secondOther = member(ServiceId.from(UUID.randomUUID()))
val updateMessageContextCapture = ArgumentCaptor.forClass(DecryptedGroupV2Context::class.java)
profileAndMessageHelper.masterKey = masterKey
doCallRealMethod().`when`(profileAndMessageHelper).insertUpdateMessages(anyLong(), anyOrNull(), any())
doNothing().`when`(profileAndMessageHelper).storeMessage(updateMessageContextCapture.capture(), anyLong())
val updateMessageContextArgs = mutableListOf<DecryptedGroupV2Context>()
every { profileAndMessageHelper.insertUpdateMessages(any(), any(), any()) } answers { callOriginal() }
every { profileAndMessageHelper.storeMessage(capture(updateMessageContextArgs), any()) } returns Unit
given {
localState(
@@ -513,8 +512,7 @@ class GroupsV2StateProcessorTest {
assertThat("local should update to server", result.groupState, `is`(GroupsV2StateProcessor.GroupState.GROUP_UPDATED))
assertThat("members contains second other", result.latestServer!!.membersList, hasItem(secondOther))
val allUpdateMessageContexts = updateMessageContextCapture.allValues
assertThat("group update messages contains new member add", allUpdateMessageContexts.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
assertThat("group update messages contains new member add", updateMessageContextArgs.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
}
/**
@@ -525,10 +523,11 @@ class GroupsV2StateProcessorTest {
fun missedMemberAddResolvesWithForcedUpdate() {
val secondOther = member(ServiceId.from(UUID.randomUUID()))
val updateMessageContextCapture = ArgumentCaptor.forClass(DecryptedGroupV2Context::class.java)
profileAndMessageHelper.masterKey = masterKey
doCallRealMethod().`when`(profileAndMessageHelper).insertUpdateMessages(anyLong(), anyOrNull(), any())
doNothing().`when`(profileAndMessageHelper).storeMessage(updateMessageContextCapture.capture(), anyLong())
val updateMessageContextArgs = mutableListOf<DecryptedGroupV2Context>()
every { profileAndMessageHelper.insertUpdateMessages(any(), any(), any()) } answers { callOriginal() }
every { profileAndMessageHelper.storeMessage(capture(updateMessageContextArgs), any()) } returns Unit
given {
localState(
@@ -548,12 +547,11 @@ class GroupsV2StateProcessorTest {
assertThat("members contains second other", result.latestServer!!.membersList, hasItem(secondOther))
assertThat("title should be updated", result.latestServer!!.title, `is`("Changed"))
val allUpdateMessageContexts = updateMessageContextCapture.allValues
assertThat("group update messages contains new member add", allUpdateMessageContexts.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
assertThat("group update messages contains new member add", updateMessageContextArgs.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
assertThat(
"group update messages contains title change",
allUpdateMessageContexts.map { it.change.newTitle },
updateMessageContextArgs.map { it.change.newTitle },
hasItem(both<DecryptedString>(notNullValue()).and(hasProperty("value", `is`("Changed"))))
)
}
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobmanager.migrations;
import org.junit.Test;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
@@ -34,7 +35,7 @@ public class SenderKeyDistributionSendJobRecipientMigrationTest {
.putBlobAsString("group_id", GROUP_ID.getDecodedId())
.build());
GroupTable.GroupRecord mockGroup = mock(GroupTable.GroupRecord.class);
GroupRecord mockGroup = mock(GroupRecord.class);
when(mockGroup.getRecipientId()).thenReturn(RecipientId.from(2));
when(mockDatabase.getGroup(GROUP_ID)).thenReturn(Optional.of(mockGroup));
@@ -92,6 +92,16 @@ fun <T> Cursor.readToSingleObject(serializer: Serializer<T, Cursor>): T? {
}
}
fun <T> Cursor.readToSingleObject(mapper: (Cursor) -> T): T? {
return use {
if (it.moveToFirst()) {
mapper(it)
} else {
null
}
}
}
@JvmOverloads
fun Cursor.readToSingleInt(defaultValue: Int = 0): Int {
return use {