mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 21:15:48 +00:00
Update conversation header with group members.
This commit is contained in:
committed by
Cody Henthorne
parent
41e0f2193a
commit
da3fc408f8
@@ -162,17 +162,17 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
binding.messageRequestAbout.setVisibility(TextUtils.isEmpty(about) || recipient.isReleaseNotes() ? GONE : VISIBLE);
|
||||
}
|
||||
|
||||
public void setSubtitle(@NonNull CharSequence subtitle, @DrawableRes int iconRes, @Nullable Runnable onClick) {
|
||||
public void setSubtitle(@NonNull CharSequence subtitle, @DrawableRes int iconRes, @Nullable String substring, @Nullable Runnable onClick) {
|
||||
if (TextUtils.isEmpty(subtitle)) {
|
||||
hideSubtitle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (onClick != null) {
|
||||
if (onClick != null && substring != null) {
|
||||
binding.messageRequestSubtitle.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
CharSequence builder = SpanUtil.clickSubstring(
|
||||
subtitle,
|
||||
subtitle,
|
||||
substring,
|
||||
listener -> onClick.run(),
|
||||
ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface),
|
||||
true
|
||||
@@ -304,6 +304,11 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
}
|
||||
}
|
||||
|
||||
if (getBackground() != null) {
|
||||
ViewUtil.setPaddingTop(binding.messageRequestInfo, 0);
|
||||
ViewUtil.setPaddingBottom(binding.messageRequestInfo, getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_header_padding));
|
||||
}
|
||||
|
||||
int padding = visibleCount == 1 ? getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_header_padding) : getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_header_padding_expanded);
|
||||
ViewUtil.setPaddingStart(binding.messageRequestInfo, padding);
|
||||
ViewUtil.setPaddingEnd(binding.messageRequestInfo, padding);
|
||||
|
||||
@@ -531,7 +531,7 @@ class ConversationAdapterV2(
|
||||
}
|
||||
|
||||
inner class ThreadHeaderViewHolder(itemView: View) : MappingViewHolder<ThreadHeader>(itemView) {
|
||||
private val conversationBanner: ConversationHeaderView = itemView as ConversationHeaderView
|
||||
private val conversationBanner: ConversationHeaderView = itemView.findViewById(R.id.header)
|
||||
|
||||
override fun bind(model: ThreadHeader) {
|
||||
val (recipient, groupInfo, sharedGroups, messageRequestState) = model.recipientInfo
|
||||
@@ -570,19 +570,16 @@ class ConversationAdapterV2(
|
||||
conversationBanner.hideUnverifiedNameSubtitle()
|
||||
}
|
||||
|
||||
if (groupInfo.pendingMemberCount > 0) {
|
||||
val invited = context.resources.getQuantityString(R.plurals.MessageRequestProfileView_invited, groupInfo.pendingMemberCount, groupInfo.pendingMemberCount)
|
||||
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members_and_invited, groupInfo.fullMemberCount, groupInfo.fullMemberCount, invited), R.drawable.symbol_group_compact_16) { goToGroupSettings(recipient) }
|
||||
} else if (groupInfo.fullMemberCount > 0) {
|
||||
if (groupInfo.fullMemberCount > 0 || groupInfo.pendingMemberCount > 0) {
|
||||
if (groupInfo.fullMemberCount == 1 && recipient.isActiveGroup) {
|
||||
conversationBanner.hideUnverifiedNameSubtitle()
|
||||
}
|
||||
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members, groupInfo.fullMemberCount, groupInfo.fullMemberCount), R.drawable.symbol_group_compact_16) { goToGroupSettings(recipient) }
|
||||
setSubtitle(context, groupInfo.pendingMemberCount, groupInfo.fullMemberCount, groupInfo.membersPreview, recipient)
|
||||
} else {
|
||||
conversationBanner.hideSubtitle()
|
||||
}
|
||||
} else if (isSelf) {
|
||||
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation), R.drawable.symbol_note_compact_16, null)
|
||||
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation), R.drawable.symbol_note_compact_16, null, null)
|
||||
} else {
|
||||
if ((recipient.profileName.toString() == recipient.getDisplayName(context)) && recipient.nickname.isEmpty && !recipient.isSystemContact) {
|
||||
conversationBanner.setUnverifiedNameSubtitle(R.drawable.symbol_person_question_16, false) {
|
||||
@@ -596,7 +593,7 @@ class ConversationAdapterV2(
|
||||
if (subtitle == null || subtitle == title) {
|
||||
conversationBanner.hideSubtitle()
|
||||
} else {
|
||||
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_phone_compact_16, null)
|
||||
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_phone_compact_16, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,6 +638,35 @@ class ConversationAdapterV2(
|
||||
conversationBanner.updateOutlineBoxSize()
|
||||
}
|
||||
|
||||
private fun setSubtitle(context: Context, pendingMemberCount: Int, size: Int, members: List<Recipient>, recipient: Recipient) {
|
||||
val names = members.map { member -> member.getDisplayName(context) }
|
||||
val otherMembers = if (size > 3) context.resources.getQuantityString(R.plurals.MessageRequestProfileView_other_members, size - 3, size - 3) else null
|
||||
val membersSubtitle = if (recipient.isActiveGroup) {
|
||||
when (size) {
|
||||
1 -> context.getString(R.string.MessageRequestProfileView_group_members_zero)
|
||||
2 -> context.getString(R.string.MessageRequestProfileView_group_members_one_and_you, names[0])
|
||||
3 -> context.getString(R.string.MessageRequestProfileView_group_members_two_and_you, names[0], names[1])
|
||||
else -> context.getString(R.string.MessageRequestProfileView_group_members_other, names[0], names[1], names[2], otherMembers)
|
||||
}
|
||||
} else {
|
||||
when (size) {
|
||||
0 -> context.getString(R.string.MessageRequestProfileView_group_members_zero)
|
||||
1 -> context.getString(R.string.MessageRequestProfileView_group_members_one, names[0])
|
||||
2 -> context.getString(R.string.MessageRequestProfileView_group_members_two, names[0], names[1])
|
||||
3 -> context.getString(R.string.MessageRequestProfileView_group_members_three, names[0], names[1], names[2])
|
||||
else -> context.getString(R.string.MessageRequestProfileView_group_members_other, names[0], names[1], names[2], otherMembers)
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingMemberCount > 0) {
|
||||
val invited = context.resources.getQuantityString(R.plurals.MessageRequestProfileView_invited, pendingMemberCount, pendingMemberCount)
|
||||
val subtitle = context.getString(R.string.MessageRequestProfileView_member_names_and_invited, membersSubtitle, invited)
|
||||
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_group_compact_16, otherMembers) { goToGroupSettings(recipient) }
|
||||
} else {
|
||||
conversationBanner.setSubtitle(membersSubtitle, R.drawable.symbol_group_compact_16, otherMembers) { goToGroupSettings(recipient) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDescription(context: Context, sharedGroups: List<String>): String {
|
||||
return when (sharedGroups.size) {
|
||||
0 -> context.getString(R.string.ConversationUpdateItem_no_groups_in_common_review_requests_carefully)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.thoughtcrime.securesms.messagerequests
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
|
||||
/**
|
||||
* Group info needed to show message request state UX.
|
||||
*/
|
||||
@@ -7,7 +9,8 @@ class GroupInfo(
|
||||
val fullMemberCount: Int = 0,
|
||||
val pendingMemberCount: Int = 0,
|
||||
val description: String = "",
|
||||
val hasExistingContacts: Boolean = false
|
||||
val hasExistingContacts: Boolean = false,
|
||||
val membersPreview: List<Recipient> = emptyList()
|
||||
) {
|
||||
companion object {
|
||||
@JvmField
|
||||
|
||||
@@ -35,10 +35,12 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
@@ -49,6 +51,7 @@ public final class MessageRequestRepository {
|
||||
|
||||
private static final String TAG = Log.tag(MessageRequestRepository.class);
|
||||
private static final int MIN_GROUPS_THRESHOLD = 2;
|
||||
private static final int MAX_MEMBER_NAMES = 3;
|
||||
|
||||
private final Context context;
|
||||
private final Executor executor;
|
||||
@@ -65,20 +68,17 @@ public final class MessageRequestRepository {
|
||||
GroupInfo groupInfo = GroupInfo.ZERO;
|
||||
|
||||
if (groupRecord.isPresent()) {
|
||||
boolean groupHasExistingContacts = false;
|
||||
List<Recipient> recipients = Recipient.resolvedList(groupRecord.get().getMembers());
|
||||
if (groupRecord.get().isV2Group()) {
|
||||
List<Recipient> recipients = Recipient.resolvedList(groupRecord.get().getMembers());
|
||||
for (Recipient recipient : recipients) {
|
||||
if ((recipient.isProfileSharing() || recipient.isSystemContact()) && !recipient.isSelf()) {
|
||||
groupHasExistingContacts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean groupHasExistingContacts = recipients.stream().filter(r -> !r.isSelf()).anyMatch(r -> r.isProfileSharing() || r.isSystemContact());
|
||||
List<Recipient> membersPreview = recipients.stream().filter(r -> !r.isSelf()).limit(MAX_MEMBER_NAMES).collect(Collectors.toList());
|
||||
DecryptedGroup decryptedGroup = groupRecord.get().requireV2GroupProperties().getDecryptedGroup();
|
||||
|
||||
DecryptedGroup decryptedGroup = groupRecord.get().requireV2GroupProperties().getDecryptedGroup();
|
||||
groupInfo = new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description, groupHasExistingContacts);
|
||||
groupInfo = new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description, groupHasExistingContacts, membersPreview);
|
||||
} else {
|
||||
groupInfo = new GroupInfo(groupRecord.get().getMembers().size(), 0, "", false);
|
||||
List<Recipient> membersPreview = recipients.stream().filter(r -> !r.isSelf()).limit(MAX_MEMBER_NAMES).collect(Collectors.toList());
|
||||
|
||||
groupInfo = new GroupInfo(groupRecord.get().getMembers().size(), 0, "", false, membersPreview);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,10 +97,14 @@
|
||||
|
||||
<View
|
||||
android:id="@+id/message_request_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:foregroundGravity="center"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:background="@color/signal_dark_colorTransparentInverse2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/message_request_about" />
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -2,12 +2,21 @@
|
||||
~ Copyright 2023 Signal Messenger, LLC
|
||||
~ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
<org.thoughtcrime.securesms.conversation.ConversationHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="22dp"
|
||||
android:paddingBottom="4dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:viewBindingIgnore="true" />
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.ConversationHeaderView
|
||||
android:id="@+id/header"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:paddingTop="22dp"
|
||||
android:paddingBottom="4dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:viewBindingIgnore="true" />
|
||||
|
||||
</FrameLayout>
|
||||
@@ -2139,6 +2139,27 @@
|
||||
<item quantity="one">%d additional group</item>
|
||||
<item quantity="other">%d additional groups</item>
|
||||
</plurals>
|
||||
<!-- Describes the names of members in a group. MessageRequestProfileView_group_members_* is nested in the first parentheses. MessageRequestProfileView_invited is nested in the second -->
|
||||
<string name="MessageRequestProfileView_member_names_and_invited">%1$s (%2$s)</string>
|
||||
<!-- Text for an empty group or when you are the only member -->
|
||||
<string name="MessageRequestProfileView_group_members_zero">No other group members yet</string>
|
||||
<!-- Text for a group with one member (not you). %1$s is their name -->
|
||||
<string name="MessageRequestProfileView_group_members_one">%1$s</string>
|
||||
<!-- Text for a 2 member group you are in. %1$s is the name of the other member -->
|
||||
<string name="MessageRequestProfileView_group_members_one_and_you">%1$s and you</string>
|
||||
<!-- Text for a 2 member group you are not in. %1$s and %2$s are the members' names -->
|
||||
<string name="MessageRequestProfileView_group_members_two">%1$s and %2$s</string>
|
||||
<!-- Text for a 3 member group you are in. %1$s and %2$s are the members' names -->
|
||||
<string name="MessageRequestProfileView_group_members_two_and_you">%1$s, %2$s, and you</string>
|
||||
<!-- Text for a 3 member group you are not in. %1$s, %2$s, %3$s are the names of the other members. -->
|
||||
<string name="MessageRequestProfileView_group_members_three">%1$s, %2$s, and %3$s</string>
|
||||
<!-- Text for a 3+ member group. %1$s, %2$s, %3$s are member names. %4$s is the string key MessageRequestProfileView_other_members-->
|
||||
<string name="MessageRequestProfileView_group_members_other">%1$s, %2$s, %3$s, and %4$s</string>
|
||||
<!-- Nested in MessageRequestProfileView_group_members_other that shows how many members (besides three) are in the group -->
|
||||
<plurals name="MessageRequestProfileView_other_members">
|
||||
<item quantity="one">%1$d other</item>
|
||||
<item quantity="other">%1$d others</item>
|
||||
</plurals>
|
||||
<!-- Button label to report spam for a conversation when in a message request state -->
|
||||
<string name="MessageRequestBottomView_report">Report…</string>
|
||||
<!-- Alert dialog title to accept a message request -->
|
||||
|
||||
Reference in New Issue
Block a user