Convert gv2 update messages to backup distinct protos.

This commit is contained in:
Clark
2024-01-24 15:53:52 -05:00
committed by Nicholas Tinsley
parent 0036b8e2d6
commit 98865d61dd
15 changed files with 1019 additions and 81 deletions

View File

@@ -84,7 +84,12 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
if (!MessageTypes.isGroupV2(record.getType())) {
needsResolve.add(record.getRecipient().getId());
} else if (MessageTypes.isGroupUpdate(record.getType())) {
UpdateDescription description = MessageRecord.getGv2ChangeDescription(ApplicationDependencies.getApplication(), record.getBody(), null);
UpdateDescription description;
if (record.getMessageExtras() != null) {
description = MessageRecord.getGv2ChangeDescription(ApplicationDependencies.getApplication(), record.getMessageExtras(), null);
} else {
description = MessageRecord.getGv2ChangeDescription(ApplicationDependencies.getApplication(), record.getBody(), null);
}
needsResolve.addAll(description.getMentioned().stream().map(RecipientId::from).collect(Collectors.toList()));
}
}

View File

@@ -572,7 +572,11 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
} else if (MessageTypes.isGroupUpdate(thread.getType())) {
if (thread.getRecipient().isPushV2Group()) {
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody(), null), defaultTint);
if (thread.getMessageExtras() != null) {
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getMessageExtras(), null), defaultTint);
} else {
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody(), null), defaultTint);
}
} else {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_group_updated), R.drawable.ic_update_group_16, defaultTint);
}

View File

@@ -108,6 +108,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails
import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent
@@ -207,6 +208,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
const val LATEST_REVISION_ID = "latest_revision_id"
const val ORIGINAL_MESSAGE_ID = "original_message_id"
const val REVISION_NUMBER = "revision_number"
const val MESSAGE_EXTRAS = "message_extras"
const val QUOTE_NOT_PRESENT_ID = 0L
const val QUOTE_TARGET_MISSING_ID = -1L
@@ -264,7 +266,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
$SCHEDULED_DATE INTEGER DEFAULT -1,
$LATEST_REVISION_ID INTEGER DEFAULT NULL REFERENCES $TABLE_NAME ($ID) ON DELETE CASCADE,
$ORIGINAL_MESSAGE_ID INTEGER DEFAULT NULL REFERENCES $TABLE_NAME ($ID) ON DELETE CASCADE,
$REVISION_NUMBER INTEGER DEFAULT 0
$REVISION_NUMBER INTEGER DEFAULT 0,
$MESSAGE_EXTRAS BLOB DEFAULT NULL
)
"""
@@ -343,7 +346,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
SCHEDULED_DATE,
LATEST_REVISION_ID,
ORIGINAL_MESSAGE_ID,
REVISION_NUMBER
REVISION_NUMBER,
MESSAGE_EXTRAS
)
private val MMS_PROJECTION: Array<String> = MMS_PROJECTION_BASE + "NULL AS ${AttachmentTable.ATTACHMENT_JSON_ALIAS}"
@@ -4985,6 +4989,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val originalMessageId: MessageId? = cursor.requireLong(ORIGINAL_MESSAGE_ID).let { if (it == 0L) null else MessageId(it) }
val editCount = cursor.requireInt(REVISION_NUMBER)
val isRead = cursor.requireBoolean(READ)
val messageExtraBytes = cursor.requireBlob(MESSAGE_EXTRAS)
val messageExtras = if (messageExtraBytes != null) MessageExtras.ADAPTER.decode(messageExtraBytes) else null
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
hasReadReceipt = false
@@ -5072,7 +5078,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
latestRevisionId,
originalMessageId,
editCount,
isRead
isRead,
messageExtras
)
}

View File

@@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras
import org.thoughtcrime.securesms.database.model.serialize
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.groups.BadGroupIdException
@@ -97,6 +98,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
const val SNIPPET_URI = "snippet_uri"
const val SNIPPET_CONTENT_TYPE = "snippet_content_type"
const val SNIPPET_EXTRAS = "snippet_extras"
const val SNIPPET_MESSAGE_EXTRAS = "snippet_message_extras"
const val ARCHIVED = "archived"
const val STATUS = "status"
const val HAS_DELIVERY_RECEIPT = "has_delivery_receipt"
@@ -137,7 +139,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
$LAST_SCROLLED INTEGER DEFAULT 0,
$PINNED INTEGER DEFAULT 0,
$UNREAD_SELF_MENTION_COUNT INTEGER DEFAULT 0,
$ACTIVE INTEGER DEFAULT 0
$ACTIVE INTEGER DEFAULT 0,
$SNIPPET_MESSAGE_EXTRAS BLOB DEFAULT NULL
)
"""
@@ -164,6 +167,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
SNIPPET_URI,
SNIPPET_CONTENT_TYPE,
SNIPPET_EXTRAS,
SNIPPET_MESSAGE_EXTRAS,
ARCHIVED,
STATUS,
HAS_DELIVERY_RECEIPT,
@@ -223,7 +227,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
expiresIn: Long,
readReceiptCount: Int,
unreadCount: Int,
unreadMentionCount: Int
unreadMentionCount: Int,
messageExtras: MessageExtras?
) {
var extraSerialized: String? = null
@@ -249,7 +254,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
EXPIRES_IN to expiresIn,
ACTIVE to 1,
UNREAD_COUNT to unreadCount,
UNREAD_SELF_MENTION_COUNT to unreadMentionCount
UNREAD_SELF_MENTION_COUNT to unreadMentionCount,
SNIPPET_MESSAGE_EXTRAS to messageExtras?.encode()
)
writableDatabase
@@ -1479,7 +1485,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
expiresIn = 0,
readReceiptCount = 0,
unreadCount = 0,
unreadMentionCount = 0
unreadMentionCount = 0,
messageExtras = null
)
}
return@withinTransaction true
@@ -1508,7 +1515,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
expiresIn = record.expiresIn,
readReceiptCount = record.hasReadReceipt().toInt(),
unreadCount = unreadCount,
unreadMentionCount = unreadMentionCount
unreadMentionCount = unreadMentionCount,
messageExtras = record.messageExtras
)
if (notifyListeners) {
@@ -1667,6 +1675,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
SNIPPET_URI to null,
SNIPPET_CONTENT_TYPE to null,
SNIPPET_EXTRAS to null,
SNIPPET_MESSAGE_EXTRAS to null,
UNREAD_COUNT to 0,
ARCHIVED to 0,
STATUS to 0,
@@ -1898,6 +1907,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val hasReadReceipt = TextSecurePreferences.isReadReceiptsEnabled(context) && cursor.requireBoolean(HAS_READ_RECEIPT)
val extraString = cursor.getString(cursor.getColumnIndexOrThrow(SNIPPET_EXTRAS))
val messageExtras = cursor.getBlob(cursor.getColumnIndexOrThrow(SNIPPET_MESSAGE_EXTRAS))
val extra: Extra? = if (extraString != null) {
try {
val jsonObject = SaneJSONObject(JSONObject(extraString))

View File

@@ -74,6 +74,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V213_FixUsernameInE
import org.thoughtcrime.securesms.database.helpers.migration.V214_PhoneNumberSharingColumn
import org.thoughtcrime.securesms.database.helpers.migration.V215_RemoveAttachmentUniqueId
import org.thoughtcrime.securesms.database.helpers.migration.V216_PhoneNumberDiscoverable
import org.thoughtcrime.securesms.database.helpers.migration.V217_MessageTableExtrasColumn
/**
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
@@ -150,10 +151,11 @@ object SignalDatabaseMigrations {
213 to V213_FixUsernameInE164Column,
214 to V214_PhoneNumberSharingColumn,
215 to V215_RemoveAttachmentUniqueId,
216 to V216_PhoneNumberDiscoverable
216 to V216_PhoneNumberDiscoverable,
217 to V217_MessageTableExtrasColumn
)
const val DATABASE_VERSION = 216
const val DATABASE_VERSION = 217
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import net.zetetic.database.sqlcipher.SQLiteDatabase
/**
* Adds a message_extras column to the messages table. This allows us to
* store extra data for messages in a more future proof and structured way.
*/
@Suppress("ClassName")
object V217_MessageTableExtrasColumn : SignalDatabaseMigration {
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("ALTER TABLE message ADD COLUMN message_extras BLOB DEFAULT NULL")
db.execSQL("ALTER TABLE thread ADD COLUMN snippet_message_extras BLOB DEFAULT NULL")
}
}

View File

@@ -25,17 +25,53 @@ import org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemov
import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
import org.signal.storageservice.protos.groups.local.EnabledState;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.backup.v2.proto.GenericGroupUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupAdminStatusUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupAnnouncementOnlyChangeUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupAttributesAccessLevelChangeUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupAvatarUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupChangeChatUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupCreationUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupDescriptionUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInvitationAcceptedUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInvitationDeclinedUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInvitationRevokedUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInviteLinkAdminApprovalUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInviteLinkDisabledUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInviteLinkEnabledUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupInviteLinkResetUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupJoinRequestApprovalUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupJoinRequestCanceledUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupJoinRequestUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupMemberAddedUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupMemberJoinedUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupMemberLeftUpdate;
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.GroupUnknownInviteeUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2AccessLevel;
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2MigrationDroppedMembersUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2MigrationInvitedMembersUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2MigrationSelfInvitedUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.GroupV2MigrationUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.SelfInvitedOtherUserToGroupUpdate;
import org.thoughtcrime.securesms.backup.v2.proto.SelfInvitedToGroupUpdate;
import org.thoughtcrime.securesms.database.model.databaseprotos.GV2UpdateDescription;
import org.thoughtcrime.securesms.groups.GV2AccessLevelUtil;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceIds;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -104,6 +140,485 @@ final class GroupsV2UpdateMessageProducer {
}
}
List<UpdateDescription> describeChanges(@NonNull List<GroupChangeChatUpdate.Update> groupUpdates) {
List<UpdateDescription> updates = new LinkedList<>();
for (GroupChangeChatUpdate.Update update : groupUpdates) {
describeUpdate(update, updates);
}
return updates;
}
private void describeUpdate(@NonNull GroupChangeChatUpdate.Update update, @NonNull List<UpdateDescription> updates) {
if (update.genericGroupUpdate != null) {
describeGenericGroupUpdate(update.genericGroupUpdate, updates);
} else if (update.groupCreationUpdate != null) {
describeGroupCreationUpdate(update.groupCreationUpdate, updates);
} else if (update.groupNameUpdate != null) {
describeGroupNameUpdate(update.groupNameUpdate, updates);
} else if (update.groupAvatarUpdate != null) {
describeAvatarChange(update.groupAvatarUpdate, updates);
} else if (update.groupDescriptionUpdate != null) {
describeDescriptionChange(update.groupDescriptionUpdate, updates);
} else if (update.groupMembershipAccessLevelChangeUpdate != null) {
describeGroupMembershipAccessLevelChange(update.groupMembershipAccessLevelChangeUpdate, updates);
} else if (update.groupAttributesAccessLevelChangeUpdate != null) {
describeGroupAttributesAccessLevelChange(update.groupAttributesAccessLevelChangeUpdate, updates);
} else if (update.groupAnnouncementOnlyChangeUpdate != null) {
describeGroupAnnouncementOnlyUpdate(update.groupAnnouncementOnlyChangeUpdate, updates);
} else if (update.groupAdminStatusUpdate != null) {
describeAdminStatusChange(update.groupAdminStatusUpdate, updates);
} else if (update.groupMemberLeftUpdate != null) {
describeGroupMemberLeftChange(update.groupMemberLeftUpdate, updates);
} else if (update.groupMemberRemovedUpdate != null) {
describeGroupMemberRemovedChange(update.groupMemberRemovedUpdate, updates);
} else if (update.selfInvitedToGroupUpdate != null) {
describeSelfInvitedToGroupUpdate(update.selfInvitedToGroupUpdate, updates);
} else if (update.selfInvitedOtherUserToGroupUpdate != null) {
describeSelfInvitedOtherUserToGroupUpdate(update.selfInvitedOtherUserToGroupUpdate, updates);
} else if (update.groupUnknownInviteeUpdate != null) {
describeUnknownUsersInvitedUpdate(update.groupUnknownInviteeUpdate, updates);
} else if (update.groupInvitationAcceptedUpdate != null) {
describeGroupInvitationAcceptedUpdate(update.groupInvitationAcceptedUpdate, updates);
} else if (update.groupMemberJoinedUpdate != null) {
describeGroupMemberJoinedUpdate(update.groupMemberJoinedUpdate, updates);
} else if (update.groupMemberAddedUpdate != null) {
describeGroupMemberAddedUpdate(update.groupMemberAddedUpdate, updates);
} else if (update.groupInvitationDeclinedUpdate != null) {
describeGroupInvitationDeclinedUpdate(update.groupInvitationDeclinedUpdate, updates);
} else if (update.groupInvitationRevokedUpdate != null) {
describeGroupInvitationRevokedUpdate(update.groupInvitationRevokedUpdate, updates);
} else if (update.groupJoinRequestUpdate != null) {
describeGroupJoinRequestUpdate(update.groupJoinRequestUpdate, updates);
} else if (update.groupJoinRequestApprovalUpdate != null) {
describeGroupJoinRequestApprovedUpdate(update.groupJoinRequestApprovalUpdate, updates);
} else if (update.groupJoinRequestCanceledUpdate != null) {
describeGroupJoinRequestCanceledUpdate(update.groupJoinRequestCanceledUpdate, updates);
} else if (update.groupInviteLinkResetUpdate != null) {
describeInviteLinkResetUpdate(update.groupInviteLinkResetUpdate, updates);
} else if (update.groupInviteLinkEnabledUpdate != null) {
describeInviteLinkEnabledUpdate(update.groupInviteLinkEnabledUpdate, updates);
} else if (update.groupInviteLinkDisabledUpdate != null) {
describeInviteLinkDisabledUpdate(update.groupInviteLinkDisabledUpdate, updates);
} else if (update.groupInviteLinkAdminApprovalUpdate != null) {
describeGroupInviteLinkAdminApprovalUpdate(update.groupInviteLinkAdminApprovalUpdate, updates);
} else if (update.groupV2MigrationUpdate != null) {
describeGroupV2MigrationUpdate(update.groupV2MigrationUpdate, updates);
} else if (update.groupV2MigrationDroppedMembersUpdate != null) {
describeGroupV2MigrationDroppedMembersUpdate(update.groupV2MigrationDroppedMembersUpdate, updates);
} else if (update.groupV2MigrationInvitedMembersUpdate != null) {
describeGroupV2MigrationInvitedMembersUpdate(update.groupV2MigrationInvitedMembersUpdate, updates);
} else if (update.groupV2MigrationSelfInvitedUpdate != null) {
describeGroupV2MigrationSelfInvitedUpdate(update.groupV2MigrationSelfInvitedUpdate, updates);
}
}
private void describeGroupV2MigrationSelfInvitedUpdate(@NonNull GroupV2MigrationSelfInvitedUpdate update, @NonNull List<UpdateDescription> updates) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_couldnt_be_added_to_the_new_group_and_have_been_invited_to_join), R.drawable.ic_update_group_add_16));
}
private void describeGroupV2MigrationDroppedMembersUpdate(@NonNull GroupV2MigrationDroppedMembersUpdate update, @NonNull List<UpdateDescription> updates) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_removed, update.droppedMembersCount, update.droppedMembersCount), R.drawable.ic_update_group_remove_16));
}
private void describeGroupV2MigrationInvitedMembersUpdate(@NonNull GroupV2MigrationInvitedMembersUpdate update, @NonNull List<UpdateDescription> updates) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_invited, update.invitedMembersCount, update.invitedMembersCount), R.drawable.ic_update_group_remove_16));
}
private void describeGroupV2MigrationUpdate(@NonNull GroupV2MigrationUpdate update, @NonNull List<UpdateDescription> updates) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_this_group_was_updated_to_a_new_group), R.drawable.ic_update_group_role_16));
}
private void describeGroupInviteLinkAdminApprovalUpdate(@NonNull GroupInviteLinkAdminApprovalUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
if (update.linkRequiresAdminApproval) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_on), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_off), R.drawable.ic_update_group_role_16));
}
} else {
if (selfIds.matches(update.updaterAci)) {
if (update.linkRequiresAdminApproval) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_admin_approval_for_the_group_link), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_admin_approval_for_the_group_link), R.drawable.ic_update_group_role_16));
}
} else {
if (update.linkRequiresAdminApproval) {
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_admin_approval_for_the_group_link, update.updaterAci, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_admin_approval_for_the_group_link, update.updaterAci, R.drawable.ic_update_group_role_16));
}
}
}
}
private void describeInviteLinkDisabledUpdate(@NonNull GroupInviteLinkDisabledUpdate update, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_off), R.drawable.ic_update_group_role_16));
} else {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_the_group_link), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_the_group_link, update.updaterAci, R.drawable.ic_update_group_role_16));
}
}
}
private void describeInviteLinkEnabledUpdate(@NonNull GroupInviteLinkEnabledUpdate update, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (update.updaterAci == null) {
if (update.linkRequiresAdminApproval) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_on), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_off), R.drawable.ic_update_group_role_16));
}
} else {
if (editorIsYou) {
if (update.linkRequiresAdminApproval) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_on), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_off), R.drawable.ic_update_group_role_16));
}
} else {
if (update.linkRequiresAdminApproval) {
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, update.updaterAci, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, update.updaterAci, R.drawable.ic_update_group_role_16));
}
}
}
}
private void describeInviteLinkResetUpdate(@NonNull GroupInviteLinkResetUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_reset), R.drawable.ic_update_group_role_16));
} else {
if (selfIds.matches(update.updaterAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_reset_the_group_link), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_reset_the_group_link, update.updaterAci, R.drawable.ic_update_group_role_16));
}
}
}
private void describeGroupJoinRequestCanceledUpdate(@NonNull GroupJoinRequestCanceledUpdate update, @NonNull List<UpdateDescription> updates) {
boolean requestingMemberIsYou = selfIds.matches(update.requestorAci);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_canceled_your_request_to_join_the_group), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_canceled_their_request_to_join_the_group, update.requestorAci, R.drawable.ic_update_group_decline_16));
}
}
private void describeGroupJoinRequestApprovedUpdate(@NonNull GroupJoinRequestApprovalUpdate update, @NonNull List<UpdateDescription> updates) {
boolean requestingMemberIsYou = selfIds.matches(update.requestorAci);
if (update.wasApproved) {
if (update.updaterAci == null) {
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved), R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_approved, update.requestorAci, R.drawable.ic_update_group_accept_16));
}
} else {
if (requestingMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_approved_your_request_to_join_the_group, update.updaterAci, R.drawable.ic_update_group_accept_16));
} else {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, update.requestorAci, R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_approved_a_request_to_join_the_group_from_s, update.updaterAci, update.requestorAci, R.drawable.ic_update_group_accept_16));
}
}
}
} else {
if (update.updaterAci == null) {
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_denied, update.requestorAci, R.drawable.ic_update_group_decline_16));
}
} else {
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_denied_a_request_to_join_the_group_from_s, update.updaterAci, update.requestorAci, R.drawable.ic_update_group_decline_16));
}
}
}
}
private void describeGroupJoinRequestUpdate(@NonNull GroupJoinRequestUpdate update, @NonNull List<UpdateDescription> updates) {
if (selfIds.matches(update.requestorAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group), R.drawable.ic_update_group_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_requested_to_join_via_the_group_link, update.requestorAci, R.drawable.ic_update_group_16));
}
}
private void describeGroupInvitationRevokedUpdate(@NonNull GroupInvitationRevokedUpdate update, @NonNull List<UpdateDescription> updates) {
int revokedMeCount = 0;
for (GroupInvitationRevokedUpdate.Invitee invitee : update.invitees) {
if (selfIds.matches(invitee.inviteeAci) || selfIds.matches(invitee.inviteePni)) {
revokedMeCount++;
}
}
int notMeInvitees = update.invitees.size() - revokedMeCount;
if (update.updaterAci == null) {
if (revokedMeCount > 0) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_an_admin_revoked_your_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
}
if (notMeInvitees > 0) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_d_invitations_were_revoked, notMeInvitees, notMeInvitees), R.drawable.ic_update_group_decline_16));
}
} else {
if (revokedMeCount > 0) {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_invitation_to_the_group, update.updaterAci, R.drawable.ic_update_group_decline_16));
}
if (selfIds.matches(update.updaterAci)) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notMeInvitees, notMeInvitees), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(R.plurals.MessageRecord_s_revoked_invites, notMeInvitees, update.updaterAci, notMeInvitees, R.drawable.ic_update_group_decline_16));
}
}
}
private void describeGroupInvitationDeclinedUpdate(@NonNull GroupInvitationDeclinedUpdate update, @NonNull List<UpdateDescription> updates) {
if (selfIds.matches(update.inviteeAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_declined_the_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
}
}
private void describeGroupMemberAddedUpdate(@NonNull GroupMemberAddedUpdate update, @NonNull List<UpdateDescription> updates) {
boolean newMemberIsYou = selfIds.matches(update.newMemberAci);
if (update.updaterAci == null) {
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, update.newMemberAci, R.drawable.ic_update_group_add_16));
}
} else if (update.hadOpenInvitation) {
if (selfIds.matches(update.updaterAci)) {
updates.add(updateDescription(R.string.MessageRecord_you_added_invited_member_s, update.newMemberAci, R.drawable.ic_update_group_add_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, update.updaterAci, update.newMemberAci, R.drawable.ic_update_group_add_16));
}
} else {
if (newMemberIsYou) {
updates.add(0, updateDescription(R.string.MessageRecord_s_added_you, update.updaterAci, R.drawable.ic_update_group_add_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_s, update.updaterAci, update.newMemberAci, R.drawable.ic_update_group_add_16));
}
}
}
private void describeGroupMemberJoinedUpdate(@NonNull GroupMemberJoinedUpdate update, @NonNull List<UpdateDescription> updates) {
boolean newMemberIsYou = selfIds.matches(update.newMemberAci);
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, update.newMemberAci, R.drawable.ic_update_group_add_16));
}
}
private void describeGroupInvitationAcceptedUpdate(@NonNull GroupInvitationAcceptedUpdate update, @NonNull List<UpdateDescription> updates) {
if (selfIds.matches(update.newMemberAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_accepted_invite), R.drawable.ic_update_group_accept_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_accepted_invite, update.newMemberAci, R.drawable.ic_update_group_accept_16));
}
}
private void describeUnknownUsersInvitedUpdate(@NonNull GroupUnknownInviteeUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.inviterAci == null) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_d_people_were_invited_to_the_group, update.inviteeCount, update.inviteeCount), R.drawable.ic_update_group_add_16));
} else {
updates.add(updateDescription(R.plurals.MessageRecord_s_invited_members, update.inviteeCount, update.inviterAci, update.inviteeCount, R.drawable.ic_update_group_add_16));
}
}
private void describeSelfInvitedOtherUserToGroupUpdate(@NonNull SelfInvitedOtherUserToGroupUpdate update, @NonNull List<UpdateDescription> updates) {
updates.add(updateDescription(R.string.MessageRecord_you_invited_s_to_the_group, update.inviteeServiceId, R.drawable.ic_update_group_add_16));
}
private void describeSelfInvitedToGroupUpdate(@NonNull SelfInvitedToGroupUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.inviterAci == null) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_were_invited_to_the_group), R.drawable.ic_update_group_add_16));
} else {
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, update.inviterAci, R.drawable.ic_update_group_add_16));
}
}
private void describeGenericGroupUpdate(@NonNull GenericGroupUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_was_updated), R.drawable.ic_update_group_16));
} else {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_updated_group, update.updaterAci, R.drawable.ic_update_group_16));
}
}
}
private void describeGroupCreationUpdate(@NonNull GroupCreationUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16));
} else {
if (selfIds.matches(update.updaterAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_added_you, update.updaterAci, R.drawable.ic_update_group_add_16));
}
}
}
private void describeGroupNameUpdate(@NonNull GroupNameUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_name_has_changed_to_s, StringUtil.isolateBidi(update.newGroupName)), R.drawable.ic_update_group_name_16));
} else {
String newTitle = StringUtil.isolateBidi(update.newGroupName);
if (selfIds.matches(update.updaterAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_name_to_s, newTitle), R.drawable.ic_update_group_name_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_name_to_s, update.updaterAci, newTitle, R.drawable.ic_update_group_name_16));
}
}
}
private void describeGroupMembershipAccessLevelChange(@NonNull GroupMembershipAccessLevelChangeUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.accessLevel == GroupV2AccessLevel.UNKNOWN) {
return;
}
String accessLevel = GV2AccessLevelUtil.toString(context, backupGv2AccessLevelToGroups(update.accessLevel));
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_membership_has_been_changed_to_s, accessLevel), R.drawable.ic_update_group_role_16));
} else {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_membership_to_s, accessLevel), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_membership_to_s, update.updaterAci, accessLevel, R.drawable.ic_update_group_role_16));
}
}
}
private void describeGroupAttributesAccessLevelChange(@NonNull GroupAttributesAccessLevelChangeUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.accessLevel == GroupV2AccessLevel.UNKNOWN) {
return;
}
String accessLevel = GV2AccessLevelUtil.toString(context, backupGv2AccessLevelToGroups(update.accessLevel));
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_info_has_been_changed_to_s, accessLevel), R.drawable.ic_update_group_role_16));
} else {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_info_to_s, accessLevel), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_info_to_s, update.updaterAci, accessLevel, R.drawable.ic_update_group_role_16));
}
}
}
private void describeGroupAnnouncementOnlyUpdate(@NonNull GroupAnnouncementOnlyChangeUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
if (update.isAnnouncementOnly) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
}
} else {
boolean editorIsYou = selfIds.matches(update.updaterAci);
if (update.isAnnouncementOnly) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_allow_only_admins_to_send, update.updaterAci, R.drawable.ic_update_group_role_16));
}
} else {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_allow_all_members_to_send, update.updaterAci, R.drawable.ic_update_group_role_16));
}
}
}
}
private void describeGroupMemberLeftChange(@NonNull GroupMemberLeftUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.aci == null) {
return;
}
boolean editorIsYou = selfIds.matches(update.aci);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_left_the_group), R.drawable.ic_update_group_leave_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_left_the_group, update.aci, R.drawable.ic_update_group_leave_16));
}
}
private void describeGroupMemberRemovedChange(@NonNull GroupMemberRemovedUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.removerAci == null) {
boolean removedMemberIsYou = selfIds.matches(update.removedAci);
if (removedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_in_the_group), R.drawable.ic_update_group_leave_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_is_no_longer_in_the_group, update.removedAci, R.drawable.ic_update_group_leave_16));
}
} else {
boolean editorIsYou = selfIds.matches(update.removerAci);
boolean removedMemberIsYou = selfIds.matches(update.removedAci);
if (editorIsYou) {
if (removedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_left_the_group), R.drawable.ic_update_group_leave_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_you_removed_s, update.removedAci, R.drawable.ic_update_group_remove_16));
}
} else {
if (removedMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_removed_you_from_the_group, update.removerAci, R.drawable.ic_update_group_remove_16));
} else {
if (update.removerAci.equals(update.removedAci)) {
updates.add(updateDescription(R.string.MessageRecord_s_left_the_group, update.removedAci, R.drawable.ic_update_group_leave_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_removed_s, update.removerAci, update.removedAci, R.drawable.ic_update_group_remove_16));
}
}
}
}
}
private AccessControl.AccessRequired backupGv2AccessLevelToGroups(@NonNull GroupV2AccessLevel accessLevel) {
switch (accessLevel) {
case ANY: return AccessControl.AccessRequired.ANY;
case MEMBER: return AccessControl.AccessRequired.MEMBER;
case ADMINISTRATOR: return AccessControl.AccessRequired.ADMINISTRATOR;
case UNSATISFIABLE: return AccessControl.AccessRequired.UNSATISFIABLE;
default:
case UNKNOWN: return AccessControl.AccessRequired.UNKNOWN;
}
}
List<UpdateDescription> describeChanges(@Nullable DecryptedGroup previousGroupState, @NonNull DecryptedGroupChange change) {
if (new DecryptedGroup().equals(previousGroupState)) {
previousGroupState = null;
@@ -317,6 +832,51 @@ final class GroupsV2UpdateMessageProducer {
}
}
private void describeAdminStatusChange(@NonNull GroupAdminStatusUpdate groupAdminStatusUpdate, List<UpdateDescription> updates) {
boolean changedMemberIsYou = selfIds.matches(groupAdminStatusUpdate.memberAci);
if (groupAdminStatusUpdate.updaterAci != null) {
boolean editorIsYou = selfIds.matches(groupAdminStatusUpdate.updaterAci);
if (groupAdminStatusUpdate.wasAdminStatusGranted) {
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_made_s_an_admin, groupAdminStatusUpdate.memberAci, R.drawable.ic_update_group_role_16));
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_made_you_an_admin, groupAdminStatusUpdate.updaterAci, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_made_s_an_admin, groupAdminStatusUpdate.updaterAci, groupAdminStatusUpdate.memberAci, R.drawable.ic_update_group_role_16));
}
}
} else {
if (editorIsYou) {
updates.add(updateDescription(R.string.MessageRecord_you_revoked_admin_privileges_from_s, groupAdminStatusUpdate.memberAci, R.drawable.ic_update_group_role_16));
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_admin_privileges, groupAdminStatusUpdate.updaterAci, R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_revoked_admin_privileges_from_s, groupAdminStatusUpdate.updaterAci, groupAdminStatusUpdate.memberAci, R.drawable.ic_update_group_role_16));
}
}
}
} else {
if (groupAdminStatusUpdate.wasAdminStatusGranted) {
if (changedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_now_an_admin), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_is_now_an_admin, groupAdminStatusUpdate.memberAci, R.drawable.ic_update_group_role_16));
}
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_an_admin), R.drawable.ic_update_group_role_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_is_no_longer_an_admin, groupAdminStatusUpdate.memberAci, R.drawable.ic_update_group_role_16));
}
}
}
}
private void describeInvitations(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
boolean editorIsYou = selfIds.matches(change.editorServiceIdBytes);
int notYouInviteCount = 0;
@@ -481,6 +1041,20 @@ final class GroupsV2UpdateMessageProducer {
}
}
private void describeDescriptionChange(@NonNull GroupDescriptionUpdate groupDescriptionUpdate, @NonNull List<UpdateDescription> updates) {
if (groupDescriptionUpdate.updaterAci != null) {
boolean editorIsYou = selfIds.matches(groupDescriptionUpdate.updaterAci);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_description), R.drawable.ic_update_group_name_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_description, groupDescriptionUpdate.updaterAci, R.drawable.ic_update_group_name_16));
}
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_name_has_changed_to_s, StringUtil.isolateBidi(groupDescriptionUpdate.newDescription)), R.drawable.ic_update_group_name_16));
}
}
private void describeUnknownEditorNewDescription(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.newDescription != null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_description_has_changed), R.drawable.ic_update_group_name_16));
@@ -499,6 +1073,20 @@ final class GroupsV2UpdateMessageProducer {
}
}
private void describeAvatarChange(@NonNull GroupAvatarUpdate groupAvatarUpdate, @NonNull List<UpdateDescription> updates) {
if (groupAvatarUpdate.updaterAci != null) {
boolean editorIsYou = selfIds.matches(groupAvatarUpdate.updaterAci);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_avatar), R.drawable.ic_update_group_avatar_16));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_avatar, groupAvatarUpdate.updaterAci, R.drawable.ic_update_group_avatar_16));
}
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_group_avatar_has_been_changed), R.drawable.ic_update_group_avatar_16));
}
}
private void describeUnknownEditorNewAvatar(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.newAvatar != null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_group_avatar_has_been_changed), R.drawable.ic_update_group_avatar_16));

View File

@@ -56,7 +56,8 @@ public class InMemoryMessageRecord extends MessageRecord {
false,
-1,
null,
0);
0,
null);
}
@Override

View File

@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context;
import org.thoughtcrime.securesms.database.model.databaseprotos.GroupCallUpdateDetails;
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras;
import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails;
import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent;
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
@@ -104,6 +105,7 @@ public abstract class MessageRecord extends DisplayRecord {
private final long receiptTimestamp;
private final MessageId originalMessageId;
private final int revisionNumber;
private final MessageExtras messageExtras;
protected Boolean isJumboji = null;
@@ -123,7 +125,8 @@ public abstract class MessageRecord extends DisplayRecord {
boolean viewed,
long receiptTimestamp,
@Nullable MessageId originalMessageId,
int revisionNumber)
int revisionNumber,
@Nullable MessageExtras messageExtras)
{
super(body, fromRecipient, toRecipient, dateSent, dateReceived,
threadId, deliveryStatus, hasDeliveryReceipt, type,
@@ -143,6 +146,7 @@ public abstract class MessageRecord extends DisplayRecord {
this.receiptTimestamp = receiptTimestamp;
this.originalMessageId = originalMessageId;
this.revisionNumber = revisionNumber;
this.messageExtras = messageExtras;
}
public abstract boolean isMms();
@@ -287,6 +291,10 @@ public abstract class MessageRecord extends DisplayRecord {
return selfCreatedGroup(change);
}
@Nullable public MessageExtras getMessageExtras() {
return messageExtras;
}
@VisibleForTesting
@Nullable DecryptedGroupV2Context getDecryptedGroupV2Context() {
if (!isGroupUpdate() || !isGroupV2()) {
@@ -315,6 +323,30 @@ public abstract class MessageRecord extends DisplayRecord {
try {
byte[] decoded = Base64.decode(body);
DecryptedGroupV2Context decryptedGroupV2Context = DecryptedGroupV2Context.ADAPTER.decode(decoded);
return getGv2ChangeDescription(context, decryptedGroupV2Context, recipientClickHandler);
} catch (IOException | IllegalArgumentException | IllegalStateException e) {
Log.w(TAG, "GV2 Message update detail could not be read", e);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
}
}
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull MessageExtras messageExtras, @Nullable Consumer<RecipientId> recipientClickHandler) {
if (messageExtras.gv2UpdateDescription != null) {
if (messageExtras.gv2UpdateDescription.groupChangeUpdate != null) {
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, SignalStore.account().getServiceIds(), recipientClickHandler);
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(messageExtras.gv2UpdateDescription.groupChangeUpdate.updates));
} else if (messageExtras.gv2UpdateDescription.gv2ChangeDescription != null) {
return getGv2ChangeDescription(context, messageExtras.gv2UpdateDescription.gv2ChangeDescription, recipientClickHandler);
} else {
Log.w(TAG, "GV2 Update Description missing group change update!");
}
}
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
}
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull DecryptedGroupV2Context decryptedGroupV2Context, @Nullable Consumer<RecipientId> recipientClickHandler) {
try {
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, SignalStore.account().getServiceIds(), recipientClickHandler);
if (decryptedGroupV2Context.change != null && ((decryptedGroupV2Context.groupState != null && decryptedGroupV2Context.groupState.revision != 0) || decryptedGroupV2Context.previousGroupState != null)) {
@@ -332,7 +364,7 @@ public abstract class MessageRecord extends DisplayRecord {
}
return UpdateDescription.concatWithNewLines(newGroupDescriptions);
}
} catch (IOException | IllegalArgumentException | IllegalStateException e) {
} catch (IllegalArgumentException | IllegalStateException e) {
Log.w(TAG, "GV2 Message update detail could not be read", e);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
}

View File

@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
@@ -111,12 +112,13 @@ public class MmsMessageRecord extends MessageRecord {
@Nullable MessageId latestRevisionId,
@Nullable MessageId originalMessageId,
int revisionNumber,
boolean isRead)
boolean isRead,
@Nullable MessageExtras messageExtras)
{
super(id, body, fromRecipient, fromDeviceId, toRecipient,
dateSent, dateReceived, dateServer, threadId, Status.STATUS_NONE, hasDeliveryReceipt,
mailbox, mismatches, failures, subscriptionId, expiresIn, expireStarted, hasReadReceipt,
unidentified, reactions, remoteDelete, notifiedTimestamp, viewed, receiptTimestamp, originalMessageId, revisionNumber);
unidentified, reactions, remoteDelete, notifiedTimestamp, viewed, receiptTimestamp, originalMessageId, revisionNumber, messageExtras);
this.slideDeck = slideDeck;
this.quote = quote;
@@ -299,7 +301,7 @@ public class MmsMessageRecord extends MessageRecord {
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
hasReadReceipt(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), reactions, isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), getCall(), getScheduledDate(), getLatestRevisionId(),
getOriginalMessageId(), getRevisionNumber(), isRead());
getOriginalMessageId(), getRevisionNumber(), isRead(), getMessageExtras());
}
public @NonNull MmsMessageRecord withoutQuote() {
@@ -307,7 +309,7 @@ public class MmsMessageRecord extends MessageRecord {
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
hasReadReceipt(), null, getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), getCall(), getScheduledDate(), getLatestRevisionId(),
getOriginalMessageId(), getRevisionNumber(), isRead());
getOriginalMessageId(), getRevisionNumber(), isRead(), getMessageExtras());
}
public @NonNull MmsMessageRecord withAttachments(@NonNull List<DatabaseAttachment> attachments) {
@@ -329,7 +331,7 @@ public class MmsMessageRecord extends MessageRecord {
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
hasReadReceipt(), quote, contacts, linkPreviews, isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), getCall(), getScheduledDate(), getLatestRevisionId(),
getOriginalMessageId(), getRevisionNumber(), isRead());
getOriginalMessageId(), getRevisionNumber(), isRead(), getMessageExtras());
}
public @NonNull MmsMessageRecord withPayment(@NonNull Payment payment) {
@@ -337,7 +339,7 @@ public class MmsMessageRecord extends MessageRecord {
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
hasReadReceipt(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), payment, getCall(), getScheduledDate(), getLatestRevisionId(),
getOriginalMessageId(), getRevisionNumber(), isRead());
getOriginalMessageId(), getRevisionNumber(), isRead(), getMessageExtras());
}
@@ -346,7 +348,7 @@ public class MmsMessageRecord extends MessageRecord {
getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(),
hasReadReceipt(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf,
getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), call, getScheduledDate(), getLatestRevisionId(),
getOriginalMessageId(), getRevisionNumber(), isRead());
getOriginalMessageId(), getRevisionNumber(), isRead(), getMessageExtras());
}
private static @NonNull List<Contact> updateContacts(@NonNull List<Contact> contacts, @NonNull Map<AttachmentId, DatabaseAttachment> attachmentIdMap) {

View File

@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.MessageTypes;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.ThreadTable.Extra;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.util.Preconditions;
@@ -37,26 +38,27 @@ import java.util.Objects;
*/
public final class ThreadRecord {
private final long threadId;
private final String body;
private final Recipient recipient;
private final long type;
private final long date;
private final long deliveryStatus;
private final boolean hasDeliveryReceipt;
private final boolean hasReadReceipt;
private final Uri snippetUri;
private final String contentType;
private final Extra extra;
private final boolean meaningfulMessages;
private final int unreadCount;
private final boolean forcedUnread;
private final int distributionType;
private final boolean archived;
private final long expiresIn;
private final long lastSeen;
private final boolean isPinned;
private final int unreadSelfMentionsCount;
private final long threadId;
private final String body;
private final Recipient recipient;
private final long type;
private final long date;
private final long deliveryStatus;
private final boolean hasDeliveryReceipt;
private final boolean hasReadReceipt;
private final Uri snippetUri;
private final String contentType;
private final Extra extra;
private final boolean meaningfulMessages;
private final int unreadCount;
private final boolean forcedUnread;
private final int distributionType;
private final boolean archived;
private final long expiresIn;
private final long lastSeen;
private final boolean isPinned;
private final int unreadSelfMentionsCount;
private final MessageExtras messageExtras;
private ThreadRecord(@NonNull Builder builder) {
this.threadId = builder.threadId;
@@ -79,6 +81,7 @@ public final class ThreadRecord {
this.lastSeen = builder.lastSeen;
this.isPinned = builder.isPinned;
this.unreadSelfMentionsCount = builder.unreadSelfMentionsCount;
this.messageExtras = builder.messageExtras;
}
public long getThreadId() {
@@ -189,6 +192,10 @@ public final class ThreadRecord {
return extra != null && extra.isScheduled();
}
public @Nullable MessageExtras getMessageExtras() {
return messageExtras;
}
public @Nullable RecipientId getGroupAddedBy() {
if (extra != null && extra.getGroupAddedBy() != null) return RecipientId.from(extra.getGroupAddedBy());
else return null;
@@ -287,26 +294,27 @@ public final class ThreadRecord {
}
public static class Builder {
private long threadId;
private String body;
private Recipient recipient = Recipient.UNKNOWN;
private long type;
private long date;
private long deliveryStatus;
private boolean hasDeliveryReceipt;
private boolean hasReadReceipt;
private Uri snippetUri;
private String contentType;
private Extra extra;
private boolean meaningfulMessages;
private int unreadCount;
private boolean forcedUnread;
private int distributionType;
private boolean archived;
private long expiresIn;
private long lastSeen;
private boolean isPinned;
private int unreadSelfMentionsCount;
private long threadId;
private String body;
private Recipient recipient = Recipient.UNKNOWN;
private long type;
private long date;
private long deliveryStatus;
private boolean hasDeliveryReceipt;
private boolean hasReadReceipt;
private Uri snippetUri;
private String contentType;
private Extra extra;
private boolean meaningfulMessages;
private int unreadCount;
private boolean forcedUnread;
private int distributionType;
private boolean archived;
private long expiresIn;
private long lastSeen;
private boolean isPinned;
private int unreadSelfMentionsCount;
private MessageExtras messageExtras;
public Builder(long threadId) {
this.threadId = threadId;
@@ -407,6 +415,11 @@ public final class ThreadRecord {
return this;
}
public Builder setSnippetMessageExtras(@Nullable MessageExtras messageExtras) {
this.messageExtras = messageExtras;
return this;
}
public Builder setUnreadSelfMentionsCount(int unreadSelfMentionsCount) {
this.unreadSelfMentionsCount = unreadSelfMentionsCount;
return this;