Integrate contact hiding with message requests.

This commit is contained in:
Clark 2023-04-05 16:55:05 -04:00 committed by Greyson Parrelli
parent 74877b839e
commit 48360d08d4
15 changed files with 107 additions and 16 deletions

View file

@ -93,7 +93,7 @@ public class NewConversationActivity extends ContactSelectionActivity
ContactsManagementViewModel.Factory factory = new ContactsManagementViewModel.Factory(repository); ContactsManagementViewModel.Factory factory = new ContactsManagementViewModel.Factory(repository);
contactLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> { contactLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), activityResult -> {
if (activityResult.getResultCode() == RESULT_OK) { if (activityResult.getResultCode() != RESULT_CANCELED) {
handleManualRefresh(); handleManualRefresh();
} }
}); });

View file

@ -24,12 +24,13 @@ data class ConversationData(
data class MessageRequestData @JvmOverloads constructor( data class MessageRequestData @JvmOverloads constructor(
val isMessageRequestAccepted: Boolean, val isMessageRequestAccepted: Boolean,
val isHidden: Boolean,
private val groupsInCommon: Boolean = false, private val groupsInCommon: Boolean = false,
val isGroup: Boolean = false val isGroup: Boolean = false
) { ) {
fun includeWarningUpdateMessage(): Boolean { fun includeWarningUpdateMessage(): Boolean {
return !isMessageRequestAccepted && !groupsInCommon return !isMessageRequestAccepted && !groupsInCommon && !isHidden
} }
} }
} }

View file

@ -71,6 +71,7 @@ public class ConversationDataSource implements PagedDataSource<MessageId, Conver
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
int size = getSizeInternal() + int size = getSizeInternal() +
(messageRequestData.includeWarningUpdateMessage() ? 1 : 0) + (messageRequestData.includeWarningUpdateMessage() ? 1 : 0) +
(messageRequestData.isHidden() ? 1 : 0) +
(showUniversalExpireTimerUpdate ? 1 : 0); (showUniversalExpireTimerUpdate ? 1 : 0);
Log.d(TAG, "[size(), thread " + threadId + "] " + (System.currentTimeMillis() - startTime) + " ms"); Log.d(TAG, "[size(), thread " + threadId + "] " + (System.currentTimeMillis() - startTime) + " ms");
@ -124,6 +125,10 @@ public class ConversationDataSource implements PagedDataSource<MessageId, Conver
records.add(new InMemoryMessageRecord.NoGroupsInCommon(threadId, messageRequestData.isGroup())); records.add(new InMemoryMessageRecord.NoGroupsInCommon(threadId, messageRequestData.isGroup()));
} }
if (messageRequestData.isHidden() && (start + length >= size())) {
records.add(new InMemoryMessageRecord.RemovedContactHidden(threadId));
}
if (showUniversalExpireTimerUpdate) { if (showUniversalExpireTimerUpdate) {
records.add(new InMemoryMessageRecord.UniversalExpireTimerUpdate(threadId)); records.add(new InMemoryMessageRecord.UniversalExpireTimerUpdate(threadId));
} }

View file

@ -65,7 +65,8 @@ class ConversationRepository {
long lastScrolled = metadata.getLastScrolled(); long lastScrolled = metadata.getLastScrolled();
int lastScrolledPosition = 0; int lastScrolledPosition = 0;
boolean isMessageRequestAccepted = RecipientUtil.isMessageRequestAccepted(context, threadId); boolean isMessageRequestAccepted = RecipientUtil.isMessageRequestAccepted(context, threadId);
ConversationData.MessageRequestData messageRequestData = new ConversationData.MessageRequestData(isMessageRequestAccepted); boolean isConversationHidden = RecipientUtil.isRecipientHidden(threadId);
ConversationData.MessageRequestData messageRequestData = new ConversationData.MessageRequestData(isMessageRequestAccepted, isConversationHidden);
boolean showUniversalExpireTimerUpdate = false; boolean showUniversalExpireTimerUpdate = false;
if (lastSeen > 0) { if (lastSeen > 0) {
@ -98,7 +99,7 @@ class ConversationRepository {
} else if (conversationRecipient.hasGroupsInCommon()) { } else if (conversationRecipient.hasGroupsInCommon()) {
recipientIsKnownOrHasGroupsInCommon = true; recipientIsKnownOrHasGroupsInCommon = true;
} }
messageRequestData = new ConversationData.MessageRequestData(isMessageRequestAccepted, recipientIsKnownOrHasGroupsInCommon, isGroup); messageRequestData = new ConversationData.MessageRequestData(isMessageRequestAccepted, isConversationHidden, recipientIsKnownOrHasGroupsInCommon, isGroup);
} }
if (SignalStore.settings().getUniversalExpireTimer() != 0 && if (SignalStore.settings().getUniversalExpireTimer() != 0 &&

View file

@ -569,7 +569,11 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
int defaultTint = ContextCompat.getColor(context, R.color.signal_text_secondary); int defaultTint = ContextCompat.getColor(context, R.color.signal_text_secondary);
if (!thread.isMessageRequestAccepted()) { if (!thread.isMessageRequestAccepted()) {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request), defaultTint); if (thread.isRecipientHidden()) {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_hidden_recipient), defaultTint);
} else {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request), defaultTint);
}
} else if (MessageTypes.isGroupUpdate(thread.getType())) { } else if (MessageTypes.isGroupUpdate(thread.getType())) {
if (thread.getRecipient().isPushV2Group()) { if (thread.getRecipient().isPushV2Group()) {
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody(), null), defaultTint); return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody(), null), defaultTint);

View file

@ -1583,6 +1583,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
private fun getExtrasFor(record: MessageRecord, body: ThreadBody): Extra? { private fun getExtrasFor(record: MessageRecord, body: ThreadBody): Extra? {
val threadRecipient = if (record.isOutgoing) record.recipient else getRecipientForThreadId(record.threadId) val threadRecipient = if (record.isOutgoing) record.recipient else getRecipientForThreadId(record.threadId)
val messageRequestAccepted = RecipientUtil.isMessageRequestAccepted(record.threadId, threadRecipient) val messageRequestAccepted = RecipientUtil.isMessageRequestAccepted(record.threadId, threadRecipient)
val isHidden = threadRecipient?.isHidden ?: false
val individualRecipientId = record.individualRecipient.id val individualRecipientId = record.individualRecipient.id
if (!messageRequestAccepted && threadRecipient != null) { if (!messageRequestAccepted && threadRecipient != null) {
@ -1609,7 +1610,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
} }
} }
} else { } else {
return Extra.forMessageRequest(individualRecipientId) return Extra.forMessageRequest(individualRecipientId, isHidden)
} }
} }
@ -1772,7 +1773,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
groupAddedBy = jsonObject.getString("groupAddedBy"), groupAddedBy = jsonObject.getString("groupAddedBy"),
individualRecipientId = jsonObject.getString("individualRecipientId")!!, individualRecipientId = jsonObject.getString("individualRecipientId")!!,
bodyRanges = jsonObject.getString("bodyRanges"), bodyRanges = jsonObject.getString("bodyRanges"),
isScheduled = jsonObject.getBoolean("isScheduled") isScheduled = jsonObject.getBoolean("isScheduled"),
isRecipientHidden = jsonObject.getBoolean("isRecipientHidden")
) )
} catch (exception: Exception) { } catch (exception: Exception) {
null null
@ -1855,7 +1857,10 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
val bodyRanges: String? = null, val bodyRanges: String? = null,
@field:JsonProperty @field:JsonProperty
@param:JsonProperty("isScheduled") @param:JsonProperty("isScheduled")
val isScheduled: Boolean = false val isScheduled: Boolean = false,
@field:JsonProperty
@param:JsonProperty("isRecipientHidden")
val isRecipientHidden: Boolean = false
) { ) {
fun getIndividualRecipientId(): String { fun getIndividualRecipientId(): String {
@ -1879,8 +1884,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return Extra(isRemoteDelete = true, individualRecipientId = individualRecipient.serialize()) return Extra(isRemoteDelete = true, individualRecipientId = individualRecipient.serialize())
} }
fun forMessageRequest(individualRecipient: RecipientId): Extra { fun forMessageRequest(individualRecipient: RecipientId, isHidden: Boolean = false): Extra {
return Extra(isMessageRequestAccepted = false, individualRecipientId = individualRecipient.serialize()) return Extra(isMessageRequestAccepted = false, individualRecipientId = individualRecipient.serialize(), isRecipientHidden = isHidden)
} }
fun forGroupMessageRequest(recipientId: RecipientId, individualRecipient: RecipientId): Extra { fun forGroupMessageRequest(recipientId: RecipientId, individualRecipient: RecipientId): Extra {

View file

@ -23,6 +23,7 @@ public class InMemoryMessageRecord extends MessageRecord {
private static final int NO_GROUPS_IN_COMMON_ID = -1; private static final int NO_GROUPS_IN_COMMON_ID = -1;
private static final int UNIVERSAL_EXPIRE_TIMER_ID = -2; private static final int UNIVERSAL_EXPIRE_TIMER_ID = -2;
private static final int FORCE_BUBBLE_ID = -3; private static final int FORCE_BUBBLE_ID = -3;
private static final int HIDDEN_CONTACT_WARNING_ID = -4;
private InMemoryMessageRecord(long id, private InMemoryMessageRecord(long id,
String body, String body,
@ -118,6 +119,29 @@ public class InMemoryMessageRecord extends MessageRecord {
} }
} }
public static final class RemovedContactHidden extends InMemoryMessageRecord {
public RemovedContactHidden(long threadId) {
super(HIDDEN_CONTACT_WARNING_ID, "", Recipient.UNKNOWN, threadId, 0);
}
@Override
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context, @Nullable Consumer<RecipientId> recipientClickHandler) {
return UpdateDescription.staticDescription(context.getString(R.string.ConversationUpdateItem_hidden_contact_message_to_add_back),
R.drawable.symbol_info_compact_16);
}
@Override
public boolean isUpdate() {
return true;
}
@Override
public boolean showActionButton() {
return false;
}
}
/** /**
* Show temporary update message about setting the disappearing messages timer upon first message * Show temporary update message about setting the disappearing messages timer upon first message
* send. * send.

View file

@ -222,6 +222,11 @@ public final class ThreadRecord {
else return true; else return true;
} }
public boolean isRecipientHidden() {
if (extra != null) return extra.isRecipientHidden();
else return true;
}
public boolean isPinned() { public boolean isPinned() {
return isPinned; return isPinned;
} }

View file

@ -116,6 +116,8 @@ final class MessageRequestRepository {
} else { } else {
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) { if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
return MessageRequestState.NONE; return MessageRequestState.NONE;
} else if (RecipientUtil.isRecipientHidden(threadId)) {
return MessageRequestState.INDIVIDUAL_HIDDEN;
} else { } else {
return MessageRequestState.INDIVIDUAL; return MessageRequestState.INDIVIDUAL;
} }

View file

@ -35,5 +35,8 @@ public enum MessageRequestState {
GROUP_V2_ADD, GROUP_V2_ADD,
/** A message request is needed for an individual */ /** A message request is needed for an individual */
INDIVIDUAL INDIVIDUAL,
/** A message request is needed for an individual since they have been hidden */
INDIVIDUAL_HIDDEN
} }

View file

@ -139,6 +139,12 @@ public class MessageRequestsBottomView extends ConstraintLayout {
setActiveInactiveGroups(normalButtons, blockedButtons, gv1MigrationButtons); setActiveInactiveGroups(normalButtons, blockedButtons, gv1MigrationButtons);
accept.setText(R.string.MessageRequestBottomView_accept); accept.setText(R.string.MessageRequestBottomView_accept);
break; break;
case INDIVIDUAL_HIDDEN:
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_you_removed_them_before,
HtmlUtil.bold(recipient.getShortDisplayName(getContext()))), 0));
setActiveInactiveGroups(normalButtons, blockedButtons, gv1MigrationButtons);
accept.setText(R.string.MessageRequestBottomView_accept);
break;
} }
} }

View file

@ -115,6 +115,7 @@ public class Recipient {
private final String profileAvatar; private final String profileAvatar;
private final ProfileAvatarFileDetails profileAvatarFileDetails; private final ProfileAvatarFileDetails profileAvatarFileDetails;
private final boolean profileSharing; private final boolean profileSharing;
private final boolean isHidden;
private final long lastProfileFetch; private final long lastProfileFetch;
private final String notificationChannel; private final String notificationChannel;
private final UnidentifiedAccessMode unidentifiedAccessMode; private final UnidentifiedAccessMode unidentifiedAccessMode;
@ -412,6 +413,7 @@ public class Recipient {
this.profileAvatar = null; this.profileAvatar = null;
this.profileAvatarFileDetails = ProfileAvatarFileDetails.NO_DETAILS; this.profileAvatarFileDetails = ProfileAvatarFileDetails.NO_DETAILS;
this.profileSharing = false; this.profileSharing = false;
this.isHidden = false;
this.lastProfileFetch = 0; this.lastProfileFetch = 0;
this.notificationChannel = null; this.notificationChannel = null;
this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED; this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED;
@ -466,6 +468,7 @@ public class Recipient {
this.profileAvatar = details.profileAvatar; this.profileAvatar = details.profileAvatar;
this.profileAvatarFileDetails = details.profileAvatarFileDetails; this.profileAvatarFileDetails = details.profileAvatarFileDetails;
this.profileSharing = details.profileSharing; this.profileSharing = details.profileSharing;
this.isHidden = details.isHidden;
this.lastProfileFetch = details.lastProfileFetch; this.lastProfileFetch = details.lastProfileFetch;
this.notificationChannel = details.notificationChannel; this.notificationChannel = details.notificationChannel;
this.unidentifiedAccessMode = details.unidentifiedAccessMode; this.unidentifiedAccessMode = details.unidentifiedAccessMode;
@ -833,6 +836,10 @@ public class Recipient {
return profileSharing; return profileSharing;
} }
public boolean isHidden() {
return isHidden;
}
public long getLastProfileFetchTime() { public long getLastProfileFetchTime() {
return lastProfileFetch; return lastProfileFetch;
} }
@ -1290,6 +1297,7 @@ public class Recipient {
expireMessages == other.expireMessages && expireMessages == other.expireMessages &&
Objects.equals(profileAvatarFileDetails, other.profileAvatarFileDetails) && Objects.equals(profileAvatarFileDetails, other.profileAvatarFileDetails) &&
profileSharing == other.profileSharing && profileSharing == other.profileSharing &&
isHidden == other.isHidden &&
forceSmsSelection == other.forceSmsSelection && forceSmsSelection == other.forceSmsSelection &&
Objects.equals(serviceId, other.serviceId) && Objects.equals(serviceId, other.serviceId) &&
Objects.equals(username, other.username) && Objects.equals(username, other.username) &&

View file

@ -63,6 +63,7 @@ public class RecipientDetails {
final String profileAvatar; final String profileAvatar;
final ProfileAvatarFileDetails profileAvatarFileDetails; final ProfileAvatarFileDetails profileAvatarFileDetails;
final boolean profileSharing; final boolean profileSharing;
final boolean isHidden;
final long lastProfileFetch; final long lastProfileFetch;
final boolean systemContact; final boolean systemContact;
final boolean isSelf; final boolean isSelf;
@ -122,6 +123,7 @@ public class RecipientDetails {
this.profileAvatar = record.getProfileAvatar(); this.profileAvatar = record.getProfileAvatar();
this.profileAvatarFileDetails = record.getProfileAvatarFileDetails(); this.profileAvatarFileDetails = record.getProfileAvatarFileDetails();
this.profileSharing = record.isProfileSharing(); this.profileSharing = record.isProfileSharing();
this.isHidden = record.isHidden();
this.lastProfileFetch = record.getLastProfileFetch(); this.lastProfileFetch = record.getLastProfileFetch();
this.systemContact = systemContact; this.systemContact = systemContact;
this.isSelf = isSelf; this.isSelf = isSelf;
@ -176,6 +178,7 @@ public class RecipientDetails {
this.profileAvatar = null; this.profileAvatar = null;
this.profileAvatarFileDetails = ProfileAvatarFileDetails.NO_DETAILS; this.profileAvatarFileDetails = ProfileAvatarFileDetails.NO_DETAILS;
this.profileSharing = false; this.profileSharing = false;
this.isHidden = false;
this.lastProfileFetch = 0; this.lastProfileFetch = 0;
this.systemContact = true; this.systemContact = true;
this.isSelf = false; this.isSelf = false;

View file

@ -198,6 +198,22 @@ public class RecipientUtil {
StorageSyncHelper.scheduleSyncForDataChange(); StorageSyncHelper.scheduleSyncForDataChange();
} }
@WorkerThread
public static boolean isRecipientHidden(long threadId) {
if (threadId < 0) {
return false;
}
ThreadTable threadTable = SignalDatabase.threads();
Recipient threadRecipient = threadTable.getRecipientForThreadId(threadId);
if (threadRecipient == null) {
return false;
}
return threadRecipient.isHidden();
}
/** /**
* If true, the new message request UI does not need to be shown, and it's safe to send read * If true, the new message request UI does not need to be shown, and it's safe to send read
* receipts. * receipts.
@ -281,7 +297,8 @@ public class RecipientUtil {
threadRecipient.isProfileSharing() || threadRecipient.isProfileSharing() ||
threadRecipient.isSystemContact() || threadRecipient.isSystemContact() ||
!threadRecipient.isRegistered() || !threadRecipient.isRegistered() ||
threadRecipient.isForceSmsSelection(); threadRecipient.isForceSmsSelection() ||
threadRecipient.isHidden();
} }
/** /**
@ -331,9 +348,11 @@ public class RecipientUtil {
threadRecipient.isSystemContact() || threadRecipient.isSystemContact() ||
threadRecipient.isForceSmsSelection() || threadRecipient.isForceSmsSelection() ||
!threadRecipient.isRegistered() || !threadRecipient.isRegistered() ||
hasSentMessageInThread(threadId) || (!threadRecipient.isHidden() && (
noSecureMessagesAndNoCallsInThread(threadId) || hasSentMessageInThread(threadId) ||
isPreMessageRequestThread(threadId); noSecureMessagesAndNoCallsInThread(threadId) ||
isPreMessageRequestThread(threadId))
);
} }
@WorkerThread @WorkerThread

View file

@ -1479,6 +1479,8 @@
<string name="MessageRequestBottomView_delete">Delete</string> <string name="MessageRequestBottomView_delete">Delete</string>
<string name="MessageRequestBottomView_block">Block</string> <string name="MessageRequestBottomView_block">Block</string>
<string name="MessageRequestBottomView_unblock">Unblock</string> <string name="MessageRequestBottomView_unblock">Unblock</string>
<!-- Text explaining a message request from someone you've removed before -->
<string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_you_removed_them_before">Let %1$s message you and share your name and photo with them? You have removed this person in the past.</string>
<string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept">Let %1$s message you and share your name and photo with them? They won\'t know you\'ve seen their message until you accept.</string> <string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept">Let %1$s message you and share your name and photo with them? They won\'t know you\'ve seen their message until you accept.</string>
<!-- Shown in message request flow. Describes what will happen if you unblock a Signal user --> <!-- Shown in message request flow. Describes what will happen if you unblock a Signal user -->
<string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them">Let %1$s message you and share your name and photo with them? You won\'t receive any messages until you unblock them.</string> <string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them">Let %1$s message you and share your name and photo with them? You won\'t receive any messages until you unblock them.</string>
@ -1992,6 +1994,8 @@
<string name="ThreadRecord_message_could_not_be_processed">Message could not be processed</string> <string name="ThreadRecord_message_could_not_be_processed">Message could not be processed</string>
<string name="ThreadRecord_delivery_issue">Delivery issue</string> <string name="ThreadRecord_delivery_issue">Delivery issue</string>
<string name="ThreadRecord_message_request">Message Request</string> <string name="ThreadRecord_message_request">Message Request</string>
<!-- Thread preview for a recipient that has been hidden -->
<string name="ThreadRecord_hidden_recipient">You have hidden this person, message them again to add them back to your list.</string>
<string name="ThreadRecord_photo">Photo</string> <string name="ThreadRecord_photo">Photo</string>
<string name="ThreadRecord_gif">GIF</string> <string name="ThreadRecord_gif">GIF</string>
<string name="ThreadRecord_voice_message">Voice Message</string> <string name="ThreadRecord_voice_message">Voice Message</string>
@ -2387,7 +2391,8 @@
<string name="ConversationUpdateItem_send_payment">Send payment</string> <string name="ConversationUpdateItem_send_payment">Send payment</string>
<!-- Update item button text to activate payments --> <!-- Update item button text to activate payments -->
<string name="ConversationUpdateItem_activate_payments">Activate payments</string> <string name="ConversationUpdateItem_activate_payments">Activate payments</string>
<!-- Update item alerting the user they hid this person and that they can message them to unhide them -->
<string name="ConversationUpdateItem_hidden_contact_message_to_add_back">You have removed this person, messaging them again will add them back to your list.</string>
<!-- audio_view --> <!-- audio_view -->
<string name="audio_view__play_pause_accessibility_description">Play … Pause</string> <string name="audio_view__play_pause_accessibility_description">Play … Pause</string>