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

File diff suppressed because it is too large Load Diff

View File

@@ -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()));
}

View File

@@ -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()

View File

@@ -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,

View File

@@ -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
}
}