Add support for blocked chat events.

This commit is contained in:
Michelle Tang
2025-03-12 12:18:04 -04:00
committed by Greyson Parrelli
parent 8101fcbd8d
commit f61109391a
11 changed files with 177 additions and 28 deletions

View File

@@ -78,13 +78,11 @@ public class FromTextView extends SimpleEmojiTextView {
setText(builder);
if (recipient.isBlocked()) setCompoundDrawablesRelativeWithIntrinsicBounds(getBlocked(), null, null, null);
else if (RemoteConfig.getInlinePinnedChats() && isPinned) setCompoundDrawablesRelativeWithIntrinsicBounds(getPinned(), null, null, null);
else setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0);
}
private Drawable getBlocked() {
return getDrawable(R.drawable.symbol_block_16, R.color.signal_icon_tint_secondary);
if (RemoteConfig.getInlinePinnedChats() && isPinned) {
setCompoundDrawablesRelativeWithIntrinsicBounds(getPinned(), null, null, null);
} else {
setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0);
}
}
private Drawable getMuted() {

View File

@@ -503,7 +503,8 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
thread.isOutgoingAudioCall() ||
thread.isOutgoingVideoCall() ||
thread.isVerificationStatusChange() ||
thread.isScheduledMessage())
thread.isScheduledMessage() ||
thread.getRecipient().isBlocked())
{
deliveryStatusIndicator.setNone();
alertView.setNone();
@@ -586,6 +587,8 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
} else {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request), defaultTint);
}
} else if (thread.getRecipient().isBlocked()) {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_blocked), R.drawable.symbol_block_16, defaultTint);
} else if (MessageTypes.isGroupUpdate(thread.getType())) {
if (thread.getRecipient().isPushV2Group()) {
if (thread.getMessageExtras() != null) {

View File

@@ -420,6 +420,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
$TYPE & ${MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT} = 0 AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_REPORTED_SPAM} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_BLOCKED} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_UNBLOCKED} AND
$TYPE NOT IN (
${MessageTypes.PROFILE_CHANGE_TYPE},
${MessageTypes.GV1_MIGRATION_TYPE},
@@ -1911,7 +1913,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
$TYPE != ${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE} AND
$TYPE & ${MessageTypes.GROUP_V2_LEAVE_BITS} != ${MessageTypes.GROUP_V2_LEAVE_BITS} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_REPORTED_SPAM} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED}
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_BLOCKED} AND
$TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_UNBLOCKED}
)
"""
@@ -2556,6 +2560,18 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
sentTimeMillis = timestamp,
expiresIn = expiresIn
)
} else if (MessageTypes.isBlocked(outboxType)) {
OutgoingMessage.blockedMessage(
threadRecipient = threadRecipient,
sentTimeMillis = timestamp,
expiresIn = expiresIn
)
} else if (MessageTypes.isUnblocked(outboxType)) {
OutgoingMessage.unblockedMessage(
threadRecipient = threadRecipient,
sentTimeMillis = timestamp,
expiresIn = expiresIn
)
} else {
val giftBadge: GiftBadge? = if (body != null && MessageTypes.isGiftBadge(outboxType)) {
GiftBadge.ADAPTER.decode(Base64.decode(body))
@@ -2727,6 +2743,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
!MessageTypes.isReportedSpam(type) &&
!MessageTypes.isMessageRequestAccepted(type) &&
!MessageTypes.isExpirationTimerUpdate(type) &&
!MessageTypes.isBlocked(type) &&
!MessageTypes.isUnblocked(type) &&
!retrieved.storyType.isStory &&
isNotStoryGroupReply &&
!silent
@@ -2860,8 +2878,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
fun insertMessageOutbox(
message: OutgoingMessage,
threadId: Long,
forceSms: Boolean,
insertListener: InsertListener?
forceSms: Boolean = false,
insertListener: InsertListener? = null
): Long {
return insertMessageOutbox(
message = message,
@@ -2904,7 +2922,16 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
if (message.isGroup) {
if (message.isV2Group) {
if (message.isBlocked) {
type = type or MessageTypes.GROUP_V2_BIT or MessageTypes.SPECIAL_TYPE_BLOCKED
hasSpecialType = true
} else if (message.isUnblocked) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.GROUP_V2_BIT or MessageTypes.SPECIAL_TYPE_UNBLOCKED
hasSpecialType = true
} else if (message.isV2Group) {
type = type or (MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
if (message.isJustAGroupLeave) {
@@ -2926,6 +2953,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
if (message.isStoryReaction) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.SPECIAL_TYPE_STORY_REACTION
hasSpecialType = true
}
@@ -2978,6 +3008,22 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
hasSpecialType = true
}
if (message.isBlocked && !message.isGroup) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.SPECIAL_TYPE_BLOCKED
hasSpecialType = true
}
if (message.isUnblocked && !message.isGroup) {
if (hasSpecialType) {
throw MmsException("Cannot insert message with multiple special types.")
}
type = type or MessageTypes.SPECIAL_TYPE_UNBLOCKED
hasSpecialType = true
}
val earlyDeliveryReceipts: Map<RecipientId, Receipt> = earlyDeliveryReceiptCache.remove(message.sentTimeMillis)
if (earlyDeliveryReceipts.isNotEmpty()) {

View File

@@ -248,4 +248,12 @@ public abstract class DisplayRecord {
public boolean isMessageRequestAccepted() {
return MessageTypes.isMessageRequestAccepted(type);
}
public boolean isBlocked() {
return MessageTypes.isBlocked(type);
}
public boolean isUnblocked() {
return MessageTypes.isUnblocked(type);
}
}

View File

@@ -283,6 +283,10 @@ public abstract class MessageRecord extends DisplayRecord {
return staticUpdateDescription(context.getString(R.string.MessageRecord_reported_as_spam), R.drawable.symbol_spam_16);
} else if (isMessageRequestAccepted()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_accepted_the_message_request), R.drawable.symbol_thread_16);
} else if (isBlocked()) {
return staticUpdateDescription(context.getString(isGroupV2() ? R.string.MessageRecord_you_blocked_this_group : R.string.MessageRecord_you_blocked_this_person), R.drawable.symbol_block_16);
} else if (isUnblocked()) {
return staticUpdateDescription(context.getString(isGroupV2() ? R.string.MessageRecord_you_unblocked_this_group : R.string.MessageRecord_you_unblocked_this_person) , R.drawable.symbol_thread_16);
}
return null;
@@ -688,7 +692,8 @@ public abstract class MessageRecord extends DisplayRecord {
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
isChangeNumber() || isReleaseChannelDonationRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() ||
isPaymentsRequestToActivate() || isPaymentsActivated() || isReportedSpam() || isMessageRequestAccepted();
isPaymentsRequestToActivate() || isPaymentsActivated() || isReportedSpam() || isMessageRequestAccepted() ||
isBlocked() || isUnblocked();
}
public boolean isMediaPending() {

View File

@@ -317,6 +317,8 @@ public class MmsMessageRecord extends MessageRecord {
(type & MessageTypes.KEY_EXCHANGE_MASK) == 0 &&
!isReportedSpam() &&
!isMessageRequestAccepted() &&
!isBlocked() &&
!isUnblocked() &&
storyType == StoryType.NONE &&
getDateSent() > 0 &&
(parentStoryId == null || parentStoryId.isDirectReply());

View File

@@ -1141,14 +1141,20 @@ object SyncMessageProcessor {
when (response.type) {
MessageRequestResponse.Type.ACCEPT -> {
val wasBlocked = recipient.isBlocked
SignalDatabase.recipients.setProfileSharing(recipient.id, true)
SignalDatabase.recipients.setBlocked(recipient.id, false)
SignalDatabase.messages.insertMessageOutbox(
OutgoingMessage.messageRequestAcceptMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId,
false,
null
)
if (wasBlocked) {
SignalDatabase.messages.insertMessageOutbox(
message = OutgoingMessage.unblockedMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId = threadId
)
} else {
SignalDatabase.messages.insertMessageOutbox(
message = OutgoingMessage.messageRequestAcceptMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId = threadId
)
}
}
MessageRequestResponse.Type.DELETE -> {
SignalDatabase.recipients.setProfileSharing(recipient.id, false)
@@ -1159,6 +1165,10 @@ object SyncMessageProcessor {
MessageRequestResponse.Type.BLOCK -> {
SignalDatabase.recipients.setBlocked(recipient.id, true)
SignalDatabase.recipients.setProfileSharing(recipient.id, false)
SignalDatabase.messages.insertMessageOutbox(
message = OutgoingMessage.blockedMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId = threadId
)
}
MessageRequestResponse.Type.BLOCK_AND_DELETE -> {
SignalDatabase.recipients.setBlocked(recipient.id, true)
@@ -1169,20 +1179,20 @@ object SyncMessageProcessor {
}
MessageRequestResponse.Type.SPAM -> {
SignalDatabase.messages.insertMessageOutbox(
OutgoingMessage.reportSpamMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId,
false,
null
message = OutgoingMessage.reportSpamMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId = threadId
)
}
MessageRequestResponse.Type.BLOCK_AND_SPAM -> {
SignalDatabase.recipients.setBlocked(recipient.id, true)
SignalDatabase.recipients.setProfileSharing(recipient.id, false)
SignalDatabase.messages.insertMessageOutbox(
OutgoingMessage.reportSpamMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId,
false,
null
message = OutgoingMessage.reportSpamMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId = threadId
)
SignalDatabase.messages.insertMessageOutbox(
message = OutgoingMessage.blockedMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.expiresInSeconds.toLong())),
threadId = threadId
)
}
else -> warn("Got an unknown response type! Skipping")

View File

@@ -55,6 +55,8 @@ data class OutgoingMessage(
val messageToEdit: Long = 0,
val isReportSpam: Boolean = false,
val isMessageRequestAccept: Boolean = false,
val isBlocked: Boolean = false,
val isUnblocked: Boolean = false,
val messageExtras: MessageExtras? = null
) {
@@ -435,6 +437,38 @@ data class OutgoingMessage(
)
}
/**
* Message for when you block someone
*/
@JvmStatic
fun blockedMessage(threadRecipient: Recipient, sentTimeMillis: Long, expiresIn: Long): OutgoingMessage {
return OutgoingMessage(
threadRecipient = threadRecipient,
sentTimeMillis = sentTimeMillis,
expiresIn = expiresIn,
isGroup = threadRecipient.isPushV2Group,
isBlocked = true,
isUrgent = false,
isSecure = true
)
}
/**
* Message for when you unblock someone
*/
@JvmStatic
fun unblockedMessage(threadRecipient: Recipient, sentTimeMillis: Long, expiresIn: Long): OutgoingMessage {
return OutgoingMessage(
threadRecipient = threadRecipient,
sentTimeMillis = sentTimeMillis,
expiresIn = expiresIn,
isGroup = threadRecipient.isPushV2Group,
isUnblocked = true,
isUrgent = false,
isSecure = true
)
}
@JvmStatic
fun buildMessage(slideDeck: SlideDeck, message: String): String {
return if (message.isNotEmpty() && slideDeck.body.isNotEmpty()) {

View File

@@ -24,10 +24,10 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMessage;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.ExpirationTimerUtil;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
@@ -36,6 +36,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class RecipientUtil {
@@ -172,6 +173,7 @@ public class RecipientUtil {
}
SignalDatabase.recipients().setBlocked(recipient.getId(), true);
insertBlockedUpdate(recipient, SignalDatabase.threads().getOrCreateThreadIdFor(recipient));
if (recipient.isSystemContact() || recipient.isProfileSharing() || isProfileSharedViaGroup(recipient)) {
SignalDatabase.recipients().setProfileSharing(recipient.getId(), false);
@@ -194,10 +196,37 @@ public class RecipientUtil {
SignalDatabase.recipients().setBlocked(recipient.getId(), false);
SignalDatabase.recipients().setProfileSharing(recipient.getId(), true);
insertUnblockedUpdate(recipient, SignalDatabase.threads().getOrCreateThreadIdFor(recipient));
AppDependencies.getJobManager().add(new MultiDeviceBlockedUpdateJob());
StorageSyncHelper.scheduleSyncForDataChange();
}
private static void insertBlockedUpdate(@NonNull Recipient recipient, long threadId) {
try {
SignalDatabase.messages().insertMessageOutbox(
OutgoingMessage.blockedMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds())),
threadId,
false,
null
);
} catch (MmsException e) {
Log.w(TAG, "Unable to insert blocked message", e);
}
}
private static void insertUnblockedUpdate(@NonNull Recipient recipient, long threadId) {
try {
SignalDatabase.messages().insertMessageOutbox(
OutgoingMessage.unblockedMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds())),
threadId,
false,
null
);
} catch (MmsException e) {
Log.w(TAG, "Unable to insert unblocked message", e);
}
}
@WorkerThread
public static Recipient.HiddenState getRecipientHiddenState(long threadId) {
if (threadId < 0) {