mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-04 15:35:38 +01:00
Add group terminate support.
This commit is contained in:
@@ -34,7 +34,6 @@ import org.signal.core.util.requireLong
|
||||
import org.signal.core.util.requireNonNullString
|
||||
import org.signal.core.util.requireString
|
||||
import org.signal.core.util.select
|
||||
import org.signal.core.util.toInt
|
||||
import org.signal.core.util.update
|
||||
import org.signal.core.util.withinTransaction
|
||||
import org.signal.libsignal.zkgroup.InvalidInputException
|
||||
@@ -103,7 +102,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
const val AVATAR_CONTENT_TYPE = "avatar_content_type"
|
||||
const val AVATAR_DIGEST = "avatar_digest"
|
||||
const val TIMESTAMP = "timestamp"
|
||||
const val ACTIVE = "active"
|
||||
const val IS_MEMBER = "active"
|
||||
const val TERMINATED_BY = "terminated_by"
|
||||
const val MMS = "mms"
|
||||
const val EXPECTED_V2_ID = "expected_v2_id"
|
||||
const val UNMIGRATED_V1_MEMBERS = "unmigrated_v1_members"
|
||||
@@ -133,17 +133,18 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
$AVATAR_CONTENT_TYPE TEXT DEFAULT NULL,
|
||||
$AVATAR_DIGEST BLOB DEFAULT NULL,
|
||||
$TIMESTAMP INTEGER DEFAULT 0,
|
||||
$ACTIVE INTEGER DEFAULT 1,
|
||||
$MMS INTEGER DEFAULT 0,
|
||||
$V2_MASTER_KEY BLOB DEFAULT NULL,
|
||||
$V2_REVISION BLOB DEFAULT NULL,
|
||||
$V2_DECRYPTED_GROUP BLOB DEFAULT NULL,
|
||||
$EXPECTED_V2_ID TEXT UNIQUE DEFAULT NULL,
|
||||
$UNMIGRATED_V1_MEMBERS TEXT DEFAULT NULL,
|
||||
$DISTRIBUTION_ID TEXT UNIQUE DEFAULT NULL,
|
||||
$SHOW_AS_STORY_STATE INTEGER DEFAULT ${ShowAsStoryState.IF_ACTIVE.code},
|
||||
$IS_MEMBER INTEGER DEFAULT 1,
|
||||
$MMS INTEGER DEFAULT 0,
|
||||
$V2_MASTER_KEY BLOB DEFAULT NULL,
|
||||
$V2_REVISION BLOB DEFAULT NULL,
|
||||
$V2_DECRYPTED_GROUP BLOB DEFAULT NULL,
|
||||
$EXPECTED_V2_ID TEXT UNIQUE DEFAULT NULL,
|
||||
$UNMIGRATED_V1_MEMBERS TEXT DEFAULT NULL,
|
||||
$DISTRIBUTION_ID TEXT UNIQUE DEFAULT NULL,
|
||||
$SHOW_AS_STORY_STATE INTEGER DEFAULT ${ShowAsStoryState.IF_ACTIVE.code},
|
||||
$LAST_FORCE_UPDATE_TIMESTAMP INTEGER DEFAULT 0,
|
||||
$GROUP_SEND_ENDORSEMENTS_EXPIRATION INTEGER DEFAULT 0
|
||||
$GROUP_SEND_ENDORSEMENTS_EXPIRATION INTEGER DEFAULT 0,
|
||||
$TERMINATED_BY INTEGER DEFAULT 0
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -160,7 +161,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
AVATAR_CONTENT_TYPE,
|
||||
AVATAR_DIGEST,
|
||||
TIMESTAMP,
|
||||
ACTIVE,
|
||||
IS_MEMBER,
|
||||
TERMINATED_BY,
|
||||
MMS,
|
||||
V2_MASTER_KEY,
|
||||
V2_REVISION,
|
||||
@@ -348,7 +350,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
FROM $TABLE_NAME
|
||||
INNER JOIN ${MembershipTable.TABLE_NAME} ON ${MembershipTable.TABLE_NAME}.${MembershipTable.GROUP_ID} = $TABLE_NAME.$GROUP_ID
|
||||
INNER JOIN ${ThreadTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = $TABLE_NAME.$RECIPIENT_ID
|
||||
WHERE $TABLE_NAME.$ACTIVE = 1 AND ${MembershipTable.TABLE_NAME}.${MembershipTable.RECIPIENT_ID} IN (${subquery.where})
|
||||
WHERE $TABLE_NAME.$IS_MEMBER = 1 AND $TABLE_NAME.$TERMINATED_BY = 0 AND ${MembershipTable.TABLE_NAME}.${MembershipTable.RECIPIENT_ID} IN (${subquery.where})
|
||||
GROUP BY ${MembershipTable.TABLE_NAME}.${MembershipTable.GROUP_ID}
|
||||
ORDER BY $TITLE COLLATE NOCASE ASC
|
||||
"""
|
||||
@@ -404,9 +406,9 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
}
|
||||
|
||||
query = if (includeInactive) {
|
||||
"($searchQuery) AND ($TABLE_NAME.$ACTIVE = ? OR $TABLE_NAME.$RECIPIENT_ID IN (SELECT ${ThreadTable.RECIPIENT_ID} FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.TABLE_NAME}.${ThreadTable.ACTIVE} = 1))"
|
||||
"($searchQuery) AND ($TABLE_NAME.$IS_MEMBER = ? OR $TABLE_NAME.$RECIPIENT_ID IN (SELECT ${ThreadTable.RECIPIENT_ID} FROM ${ThreadTable.TABLE_NAME} WHERE ${ThreadTable.TABLE_NAME}.${ThreadTable.ACTIVE} = 1))"
|
||||
} else {
|
||||
"($searchQuery) AND $TABLE_NAME.$ACTIVE = ?"
|
||||
"($searchQuery) AND $TABLE_NAME.$IS_MEMBER = ? AND $TABLE_NAME.$TERMINATED_BY = 0"
|
||||
}
|
||||
|
||||
queryArgs = buildArgs(*searchTokens.toTypedArray(), 1)
|
||||
@@ -495,8 +497,11 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
}
|
||||
|
||||
if (!includeInactive) {
|
||||
query += " AND $TABLE_NAME.$ACTIVE = ?"
|
||||
query += " AND $TABLE_NAME.$IS_MEMBER = ?"
|
||||
args = appendArg(args, "1")
|
||||
|
||||
query += " AND $TABLE_NAME.$TERMINATED_BY = ?"
|
||||
args = appendArg(args, "0")
|
||||
}
|
||||
|
||||
return readableDatabase
|
||||
@@ -522,22 +527,23 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
return Reader(cursor)
|
||||
}
|
||||
|
||||
fun getInactiveGroups(): Reader {
|
||||
val query = SqlUtil.buildQuery("$TABLE_NAME.$ACTIVE = ?", false.toInt())
|
||||
val select = "${joinedGroupSelect()} WHERE ${query.where}"
|
||||
|
||||
return Reader(readableDatabase.query(select, query.whereArgs))
|
||||
}
|
||||
|
||||
fun getActiveGroupCount(): Int {
|
||||
return readableDatabase
|
||||
.select("COUNT(*)")
|
||||
.from(TABLE_NAME)
|
||||
.where("$ACTIVE = ?", 1)
|
||||
.where("$IS_MEMBER = ? AND $TERMINATED_BY = ?", 1, 0)
|
||||
.run()
|
||||
.readToSingleInt(0)
|
||||
}
|
||||
|
||||
fun setTerminatedBy(groupId: GroupId, recipientId: RecipientId) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(TERMINATED_BY to recipientId.serialize())
|
||||
.where("$GROUP_ID = ?", groupId)
|
||||
.run()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun getGroupMemberIds(groupId: GroupId, memberSet: MemberSet): List<RecipientId> {
|
||||
return if (groupId.isV2) {
|
||||
@@ -688,14 +694,15 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
values.put(TIMESTAMP, System.currentTimeMillis())
|
||||
|
||||
if (groupId.isV2) {
|
||||
values.put(ACTIVE, if (groupState != null && gv2GroupActive(groupState)) 1 else 0)
|
||||
values.put(IS_MEMBER, if (groupState != null && isGroupMember(groupState)) 1 else 0)
|
||||
values.put(TERMINATED_BY, if (groupState?.terminated == true) -1 else 0)
|
||||
values.put(DISTRIBUTION_ID, DistributionId.create().toString())
|
||||
values.put(GROUP_SEND_ENDORSEMENTS_EXPIRATION, receivedGroupSendEndorsements?.expirationMs ?: 0)
|
||||
} else if (groupId.isV1) {
|
||||
values.put(ACTIVE, 1)
|
||||
values.put(IS_MEMBER, 1)
|
||||
values.put(EXPECTED_V2_ID, groupId.requireV1().deriveV2MigrationGroupId().toString())
|
||||
} else {
|
||||
values.put(ACTIVE, 1)
|
||||
values.put(IS_MEMBER, 1)
|
||||
}
|
||||
|
||||
if (groupMasterKey != null) {
|
||||
@@ -793,7 +800,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
contentValues.put(TITLE, title)
|
||||
contentValues.put(V2_REVISION, decryptedGroup.revision)
|
||||
contentValues.put(V2_DECRYPTED_GROUP, decryptedGroup.encode())
|
||||
contentValues.put(ACTIVE, if (gv2GroupActive(decryptedGroup)) 1 else 0)
|
||||
contentValues.put(IS_MEMBER, if (isGroupMember(decryptedGroup)) 1 else 0)
|
||||
contentValues.put(TERMINATED_BY, if (decryptedGroup.terminated) -1 else 0)
|
||||
|
||||
if (receivedGroupSendEndorsements != null) {
|
||||
contentValues.put(GROUP_SEND_ENDORSEMENTS_EXPIRATION, receivedGroupSendEndorsements.expirationMs)
|
||||
@@ -938,10 +946,15 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
return record.isPresent && record.get().isActive
|
||||
}
|
||||
|
||||
fun setActive(groupId: GroupId, active: Boolean) {
|
||||
fun isMember(groupId: GroupId): Boolean {
|
||||
val record = getGroup(groupId)
|
||||
return record.isPresent && record.get().isMember
|
||||
}
|
||||
|
||||
fun setMember(groupId: GroupId, isMember: Boolean) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(ACTIVE to if (active) 1 else 0)
|
||||
.values(IS_MEMBER to if (isMember) 1 else 0)
|
||||
.where("$GROUP_ID = ?", groupId)
|
||||
.run()
|
||||
}
|
||||
@@ -1079,6 +1092,12 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
}
|
||||
|
||||
override fun remapRecipient(fromId: RecipientId, toId: RecipientId) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(TERMINATED_BY to toId.toLong())
|
||||
.where("$TERMINATED_BY = ?", fromId.toLong())
|
||||
.run()
|
||||
|
||||
// Remap all recipients that would not result in conflicts
|
||||
writableDatabase.execSQL(
|
||||
"""
|
||||
@@ -1139,7 +1158,8 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
avatarId = cursor.requireLong(AVATAR_ID),
|
||||
avatarKey = cursor.requireBlob(AVATAR_KEY),
|
||||
avatarContentType = cursor.requireString(AVATAR_CONTENT_TYPE),
|
||||
isActive = cursor.requireBoolean(ACTIVE),
|
||||
isMember = cursor.requireBoolean(IS_MEMBER),
|
||||
terminatedBy = cursor.requireLong(TERMINATED_BY),
|
||||
avatarDigest = cursor.requireBlob(AVATAR_DIGEST),
|
||||
isMms = cursor.requireBoolean(MMS),
|
||||
groupMasterKeyBytes = cursor.requireBlob(V2_MASTER_KEY),
|
||||
@@ -1345,7 +1365,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
) AS active_timestamp
|
||||
FROM $TABLE_NAME INNER JOIN ${ThreadTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = $TABLE_NAME.$RECIPIENT_ID
|
||||
WHERE
|
||||
$TABLE_NAME.$ACTIVE = 1 AND
|
||||
$TABLE_NAME.$IS_MEMBER = 1 AND $TABLE_NAME.$TERMINATED_BY = 0 AND
|
||||
(
|
||||
$SHOW_AS_STORY_STATE = ${ShowAsStoryState.ALWAYS.code} OR
|
||||
(
|
||||
@@ -1394,7 +1414,7 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun gv2GroupActive(decryptedGroup: DecryptedGroup): Boolean {
|
||||
private fun isGroupMember(decryptedGroup: DecryptedGroup): Boolean {
|
||||
val aci = SignalStore.account.requireAci()
|
||||
|
||||
return decryptedGroup.members.findMemberByAci(aci).isPresent ||
|
||||
|
||||
@@ -2861,7 +2861,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
|
||||
}
|
||||
}
|
||||
|
||||
val silent = (MessageTypes.isGroupUpdate(type) && !retrieved.isGroupAdd) ||
|
||||
val silent = (MessageTypes.isGroupUpdate(type) && !retrieved.isNotifiable) ||
|
||||
retrieved.type == MessageType.IDENTITY_DEFAULT ||
|
||||
retrieved.type == MessageType.IDENTITY_VERIFIED ||
|
||||
retrieved.type == MessageType.IDENTITY_UPDATE
|
||||
|
||||
@@ -3745,7 +3745,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
val threadDatabase = threads
|
||||
val recipientsWithinInteractionThreshold: MutableSet<RecipientId> = LinkedHashSet()
|
||||
|
||||
threadDatabase.readerFor(threadDatabase.getRecentPushConversationList(-1, false)).use { reader ->
|
||||
threadDatabase.readerFor(threadDatabase.getRecentPushConversationList(-1)).use { reader ->
|
||||
var record: ThreadRecord? = reader.getNext()
|
||||
|
||||
while (record != null && record.date > lastInteractionThreshold) {
|
||||
@@ -4830,7 +4830,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
SELECT 1
|
||||
FROM ${GroupTable.MembershipTable.TABLE_NAME}
|
||||
INNER JOIN ${GroupTable.TABLE_NAME} ON ${GroupTable.TABLE_NAME}.${GroupTable.GROUP_ID} = ${GroupTable.MembershipTable.TABLE_NAME}.${GroupTable.MembershipTable.GROUP_ID}
|
||||
WHERE ${GroupTable.MembershipTable.TABLE_NAME}.${GroupTable.MembershipTable.RECIPIENT_ID} = $TABLE_NAME.$ID AND ${GroupTable.TABLE_NAME}.${GroupTable.ACTIVE} = 1 AND ${GroupTable.TABLE_NAME}.${GroupTable.MMS} = 0
|
||||
WHERE ${GroupTable.MembershipTable.TABLE_NAME}.${GroupTable.MembershipTable.RECIPIENT_ID} = $TABLE_NAME.$ID AND ${GroupTable.TABLE_NAME}.${GroupTable.IS_MEMBER} = 1 AND ${GroupTable.TABLE_NAME}.${GroupTable.TERMINATED_BY} = 0 AND ${GroupTable.TABLE_NAME}.${GroupTable.MMS} = 0
|
||||
)
|
||||
"""
|
||||
val E164_SEARCH = "(($PHONE_NUMBER_SHARING != ${PhoneNumberSharingState.DISABLED.id} OR $SYSTEM_CONTACT_URI NOT NULL) AND $E164 GLOB ?)"
|
||||
|
||||
@@ -873,7 +873,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
var where = ""
|
||||
|
||||
if (!includeInactiveGroups) {
|
||||
where += "$MEANINGFUL_MESSAGES != 0 AND (${GroupTable.TABLE_NAME}.${GroupTable.ACTIVE} IS NULL OR ${GroupTable.TABLE_NAME}.${GroupTable.ACTIVE} = 1)"
|
||||
where += "$MEANINGFUL_MESSAGES != 0 AND (${GroupTable.TABLE_NAME}.${GroupTable.IS_MEMBER} IS NULL OR (${GroupTable.TABLE_NAME}.${GroupTable.IS_MEMBER} = 1 AND ${GroupTable.TABLE_NAME}.${GroupTable.TERMINATED_BY} = 0))"
|
||||
} else {
|
||||
where += "$MEANINGFUL_MESSAGES != 0"
|
||||
}
|
||||
@@ -922,8 +922,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
return readableDatabase.rawQuery(query, null)
|
||||
}
|
||||
|
||||
fun getRecentPushConversationList(limit: Int, includeInactiveGroups: Boolean): Cursor {
|
||||
val activeGroupQuery = if (!includeInactiveGroups) " AND " + GroupTable.TABLE_NAME + "." + GroupTable.ACTIVE + " = 1" else ""
|
||||
fun getRecentPushConversationList(limit: Int): Cursor {
|
||||
val where = """
|
||||
$MEANINGFUL_MESSAGES != 0
|
||||
AND (
|
||||
@@ -931,7 +930,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
||||
OR (
|
||||
${GroupTable.TABLE_NAME}.${GroupTable.GROUP_ID} NOT NULL
|
||||
AND ${GroupTable.TABLE_NAME}.${GroupTable.MMS} = 0
|
||||
$activeGroupQuery
|
||||
AND ${GroupTable.TABLE_NAME}.${GroupTable.IS_MEMBER} = 1
|
||||
AND ${GroupTable.TABLE_NAME}.${GroupTable.TERMINATED_BY} = 0
|
||||
)
|
||||
)
|
||||
"""
|
||||
|
||||
+4
-2
@@ -161,6 +161,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V304_CallAndReplyNo
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V305_AddStoryArchivedColumn
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V306_AddRemoteDeletedColumn
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V308_AddBackRemoteDeletedColumn
|
||||
import org.thoughtcrime.securesms.database.helpers.migration.V309_GroupTerminatedColumnMigration
|
||||
import org.thoughtcrime.securesms.database.SQLiteDatabase as SignalSqliteDatabase
|
||||
|
||||
/**
|
||||
@@ -329,10 +330,11 @@ object SignalDatabaseMigrations {
|
||||
305 to V305_AddStoryArchivedColumn,
|
||||
306 to V306_AddRemoteDeletedColumn,
|
||||
// 307 to V307_RemoveRemoteDeletedColumn - Removed due to unsolvable OOM crashes. [TODO]: Attempt to fix in the future
|
||||
308 to V308_AddBackRemoteDeletedColumn
|
||||
308 to V308_AddBackRemoteDeletedColumn,
|
||||
309 to V309_GroupTerminatedColumnMigration
|
||||
)
|
||||
|
||||
const val DATABASE_VERSION = 308
|
||||
const val DATABASE_VERSION = 309
|
||||
|
||||
@JvmStatic
|
||||
fun migrate(context: Application, db: SignalSqliteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.thoughtcrime.securesms.database.helpers.migration
|
||||
|
||||
import android.app.Application
|
||||
import org.thoughtcrime.securesms.database.SQLiteDatabase
|
||||
|
||||
/**
|
||||
* Adds 'terminated_by' that stores the recipient id of the
|
||||
* admin who terminated the group, -1 if unknown, 0 if not terminated.
|
||||
*/
|
||||
@Suppress("ClassName")
|
||||
object V309_GroupTerminatedColumnMigration : SignalDatabaseMigration {
|
||||
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("ALTER TABLE groups ADD COLUMN terminated_by INTEGER DEFAULT 0")
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,8 @@ class GroupRecord(
|
||||
val avatarId: Long,
|
||||
val avatarKey: ByteArray?,
|
||||
val avatarContentType: String?,
|
||||
val isActive: Boolean,
|
||||
val isMember: Boolean,
|
||||
private val terminatedBy: Long = 0,
|
||||
val avatarDigest: ByteArray?,
|
||||
val isMms: Boolean,
|
||||
groupMasterKeyBytes: ByteArray?,
|
||||
@@ -61,6 +62,15 @@ class GroupRecord(
|
||||
}
|
||||
}
|
||||
|
||||
val isTerminated: Boolean
|
||||
get() = terminatedBy != 0L
|
||||
|
||||
val terminatedByRecipientId: RecipientId?
|
||||
get() = if (terminatedBy > 0) RecipientId.from(terminatedBy) else null
|
||||
|
||||
val isActive: Boolean
|
||||
get() = isMember && !isTerminated
|
||||
|
||||
val description: String
|
||||
get() = v2GroupProperties?.decryptedGroup?.description ?: ""
|
||||
|
||||
|
||||
+16
@@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.GroupMembershipAccessLevelChan
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupNameUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupSelfInvitationRevokedUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupSequenceOfRequestsAndCancelsUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupTerminateChangeUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupUnknownInviteeUpdate
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2AccessLevel
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.SelfInvitedOtherUserToGroupUpdate
|
||||
@@ -155,6 +156,7 @@ object GroupsV2UpdateMessageConverter {
|
||||
translateAnnouncementGroupChange(change, editorUnknown, updates)
|
||||
translatePromotePendingPniAci(selfIds, change, editorUnknown, updates)
|
||||
translateMemberRemovals(selfIds, change, editorUnknown, updates)
|
||||
translateTerminateGroup(change, editorUnknown, updates)
|
||||
if (updates.isEmpty()) {
|
||||
translateUnknownChange(change, editorUnknown, updates)
|
||||
}
|
||||
@@ -684,6 +686,20 @@ object GroupsV2UpdateMessageConverter {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun translateTerminateGroup(change: DecryptedGroupChange, editorUnknown: Boolean, updates: MutableList<GroupChangeChatUpdate.Update>) {
|
||||
if (change.terminateGroup) {
|
||||
val editorAci = if (editorUnknown) null else change.editorServiceIdBytes
|
||||
updates.add(
|
||||
GroupChangeChatUpdate.Update(
|
||||
groupTerminateChangeUpdate = GroupTerminateChangeUpdate(
|
||||
updaterAci = editorAci
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun translateUnknownChange(change: DecryptedGroupChange, editorUnknown: Boolean, updates: MutableList<GroupChangeChatUpdate.Update>) {
|
||||
updates.add(
|
||||
|
||||
+15
@@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.backup.v2.proto.GroupMemberRemovedUpdate;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupMembershipAccessLevelChangeUpdate;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupNameUpdate;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupSelfInvitationRevokedUpdate;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupTerminateChangeUpdate;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupUnknownInviteeUpdate;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2AccessLevel;
|
||||
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2MigrationDroppedMembersUpdate;
|
||||
@@ -220,6 +221,8 @@ final class GroupsV2UpdateMessageProducer {
|
||||
describeGroupExpirationTimerUpdate(update.groupExpirationTimerUpdate, updates);
|
||||
} else if (update.groupSelfInvitationRevokedUpdate != null) {
|
||||
describeGroupSelfInvitationRevokedUpdate(update.groupSelfInvitationRevokedUpdate, updates);
|
||||
} else if (update.groupTerminateChangeUpdate != null) {
|
||||
describeGroupTerminateUpdate(update.groupTerminateChangeUpdate, updates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +234,18 @@ final class GroupsV2UpdateMessageProducer {
|
||||
}
|
||||
}
|
||||
|
||||
private void describeGroupTerminateUpdate(@NonNull GroupTerminateChangeUpdate update, @NonNull List<UpdateDescription> updates) {
|
||||
if (update.updaterAci == null) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_was_terminated), Glyph.X_CIRCLE));
|
||||
} else {
|
||||
if (selfIds.matches(update.updaterAci)) {
|
||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_terminated_the_group), Glyph.X_CIRCLE));
|
||||
} else {
|
||||
updates.add(updateDescription(R.string.MessageRecord_s_terminated_the_group, update.updaterAci, Glyph.X_CIRCLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void describeGroupExpirationTimerUpdate(@NonNull GroupExpirationTimerUpdate update, @NonNull List<UpdateDescription> updates) {
|
||||
final int duration = Math.toIntExact(update.expiresInMs / 1000);
|
||||
String time = ExpirationUtil.getExpirationDisplayValue(context, duration);
|
||||
|
||||
Reference in New Issue
Block a user