Skip to content

Commit da3fc40

Browse files
mtang-signalcody-signal
authored andcommitted
Update conversation header with group members.
1 parent 41e0f21 commit da3fc40

File tree

7 files changed

+98
-30
lines changed

7 files changed

+98
-30
lines changed

app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,17 @@ public void setAbout(@NonNull Recipient recipient) {
162162
binding.messageRequestAbout.setVisibility(TextUtils.isEmpty(about) || recipient.isReleaseNotes() ? GONE : VISIBLE);
163163
}
164164

165-
public void setSubtitle(@NonNull CharSequence subtitle, @DrawableRes int iconRes, @Nullable Runnable onClick) {
165+
public void setSubtitle(@NonNull CharSequence subtitle, @DrawableRes int iconRes, @Nullable String substring, @Nullable Runnable onClick) {
166166
if (TextUtils.isEmpty(subtitle)) {
167167
hideSubtitle();
168168
return;
169169
}
170170

171-
if (onClick != null) {
171+
if (onClick != null && substring != null) {
172172
binding.messageRequestSubtitle.setMovementMethod(LinkMovementMethod.getInstance());
173173
CharSequence builder = SpanUtil.clickSubstring(
174174
subtitle,
175-
subtitle,
175+
substring,
176176
listener -> onClick.run(),
177177
ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface),
178178
true
@@ -304,6 +304,11 @@ public void updateOutlineBoxSize() {
304304
}
305305
}
306306

307+
if (getBackground() != null) {
308+
ViewUtil.setPaddingTop(binding.messageRequestInfo, 0);
309+
ViewUtil.setPaddingBottom(binding.messageRequestInfo, getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_header_padding));
310+
}
311+
307312
int padding = visibleCount == 1 ? getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_header_padding) : getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_header_padding_expanded);
308313
ViewUtil.setPaddingStart(binding.messageRequestInfo, padding);
309314
ViewUtil.setPaddingEnd(binding.messageRequestInfo, padding);

app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ class ConversationAdapterV2(
531531
}
532532

533533
inner class ThreadHeaderViewHolder(itemView: View) : MappingViewHolder<ThreadHeader>(itemView) {
534-
private val conversationBanner: ConversationHeaderView = itemView as ConversationHeaderView
534+
private val conversationBanner: ConversationHeaderView = itemView.findViewById(R.id.header)
535535

536536
override fun bind(model: ThreadHeader) {
537537
val (recipient, groupInfo, sharedGroups, messageRequestState) = model.recipientInfo
@@ -570,19 +570,16 @@ class ConversationAdapterV2(
570570
conversationBanner.hideUnverifiedNameSubtitle()
571571
}
572572

573-
if (groupInfo.pendingMemberCount > 0) {
574-
val invited = context.resources.getQuantityString(R.plurals.MessageRequestProfileView_invited, groupInfo.pendingMemberCount, groupInfo.pendingMemberCount)
575-
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members_and_invited, groupInfo.fullMemberCount, groupInfo.fullMemberCount, invited), R.drawable.symbol_group_compact_16) { goToGroupSettings(recipient) }
576-
} else if (groupInfo.fullMemberCount > 0) {
573+
if (groupInfo.fullMemberCount > 0 || groupInfo.pendingMemberCount > 0) {
577574
if (groupInfo.fullMemberCount == 1 && recipient.isActiveGroup) {
578575
conversationBanner.hideUnverifiedNameSubtitle()
579576
}
580-
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members, groupInfo.fullMemberCount, groupInfo.fullMemberCount), R.drawable.symbol_group_compact_16) { goToGroupSettings(recipient) }
577+
setSubtitle(context, groupInfo.pendingMemberCount, groupInfo.fullMemberCount, groupInfo.membersPreview, recipient)
581578
} else {
582579
conversationBanner.hideSubtitle()
583580
}
584581
} else if (isSelf) {
585-
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation), R.drawable.symbol_note_compact_16, null)
582+
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation), R.drawable.symbol_note_compact_16, null, null)
586583
} else {
587584
if ((recipient.profileName.toString() == recipient.getDisplayName(context)) && recipient.nickname.isEmpty && !recipient.isSystemContact) {
588585
conversationBanner.setUnverifiedNameSubtitle(R.drawable.symbol_person_question_16, false) {
@@ -596,7 +593,7 @@ class ConversationAdapterV2(
596593
if (subtitle == null || subtitle == title) {
597594
conversationBanner.hideSubtitle()
598595
} else {
599-
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_phone_compact_16, null)
596+
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_phone_compact_16, null, null)
600597
}
601598
}
602599

@@ -641,6 +638,35 @@ class ConversationAdapterV2(
641638
conversationBanner.updateOutlineBoxSize()
642639
}
643640

641+
private fun setSubtitle(context: Context, pendingMemberCount: Int, size: Int, members: List<Recipient>, recipient: Recipient) {
642+
val names = members.map { member -> member.getDisplayName(context) }
643+
val otherMembers = if (size > 3) context.resources.getQuantityString(R.plurals.MessageRequestProfileView_other_members, size - 3, size - 3) else null
644+
val membersSubtitle = if (recipient.isActiveGroup) {
645+
when (size) {
646+
1 -> context.getString(R.string.MessageRequestProfileView_group_members_zero)
647+
2 -> context.getString(R.string.MessageRequestProfileView_group_members_one_and_you, names[0])
648+
3 -> context.getString(R.string.MessageRequestProfileView_group_members_two_and_you, names[0], names[1])
649+
else -> context.getString(R.string.MessageRequestProfileView_group_members_other, names[0], names[1], names[2], otherMembers)
650+
}
651+
} else {
652+
when (size) {
653+
0 -> context.getString(R.string.MessageRequestProfileView_group_members_zero)
654+
1 -> context.getString(R.string.MessageRequestProfileView_group_members_one, names[0])
655+
2 -> context.getString(R.string.MessageRequestProfileView_group_members_two, names[0], names[1])
656+
3 -> context.getString(R.string.MessageRequestProfileView_group_members_three, names[0], names[1], names[2])
657+
else -> context.getString(R.string.MessageRequestProfileView_group_members_other, names[0], names[1], names[2], otherMembers)
658+
}
659+
}
660+
661+
if (pendingMemberCount > 0) {
662+
val invited = context.resources.getQuantityString(R.plurals.MessageRequestProfileView_invited, pendingMemberCount, pendingMemberCount)
663+
val subtitle = context.getString(R.string.MessageRequestProfileView_member_names_and_invited, membersSubtitle, invited)
664+
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_group_compact_16, otherMembers) { goToGroupSettings(recipient) }
665+
} else {
666+
conversationBanner.setSubtitle(membersSubtitle, R.drawable.symbol_group_compact_16, otherMembers) { goToGroupSettings(recipient) }
667+
}
668+
}
669+
644670
private fun getDescription(context: Context, sharedGroups: List<String>): String {
645671
return when (sharedGroups.size) {
646672
0 -> context.getString(R.string.ConversationUpdateItem_no_groups_in_common_review_requests_carefully)

app/src/main/java/org/thoughtcrime/securesms/messagerequests/GroupInfo.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package org.thoughtcrime.securesms.messagerequests
22

3+
import org.thoughtcrime.securesms.recipients.Recipient
4+
35
/**
46
* Group info needed to show message request state UX.
57
*/
68
class GroupInfo(
79
val fullMemberCount: Int = 0,
810
val pendingMemberCount: Int = 0,
911
val description: String = "",
10-
val hasExistingContacts: Boolean = false
12+
val hasExistingContacts: Boolean = false,
13+
val membersPreview: List<Recipient> = emptyList()
1114
) {
1215
companion object {
1316
@JvmField

app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestRepository.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@
3535
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
3636

3737
import java.io.IOException;
38+
import java.util.ArrayList;
3839
import java.util.List;
3940
import java.util.Optional;
4041
import java.util.concurrent.Executor;
4142
import java.util.concurrent.TimeUnit;
43+
import java.util.stream.Collectors;
4244

4345
import io.reactivex.rxjava3.core.Completable;
4446
import io.reactivex.rxjava3.core.Single;
@@ -49,6 +51,7 @@ public final class MessageRequestRepository {
4951

5052
private static final String TAG = Log.tag(MessageRequestRepository.class);
5153
private static final int MIN_GROUPS_THRESHOLD = 2;
54+
private static final int MAX_MEMBER_NAMES = 3;
5255

5356
private final Context context;
5457
private final Executor executor;
@@ -65,20 +68,17 @@ public MessageRequestRepository(@NonNull Context context) {
6568
GroupInfo groupInfo = GroupInfo.ZERO;
6669

6770
if (groupRecord.isPresent()) {
68-
boolean groupHasExistingContacts = false;
71+
List<Recipient> recipients = Recipient.resolvedList(groupRecord.get().getMembers());
6972
if (groupRecord.get().isV2Group()) {
70-
List<Recipient> recipients = Recipient.resolvedList(groupRecord.get().getMembers());
71-
for (Recipient recipient : recipients) {
72-
if ((recipient.isProfileSharing() || recipient.isSystemContact()) && !recipient.isSelf()) {
73-
groupHasExistingContacts = true;
74-
break;
75-
}
76-
}
73+
boolean groupHasExistingContacts = recipients.stream().filter(r -> !r.isSelf()).anyMatch(r -> r.isProfileSharing() || r.isSystemContact());
74+
List<Recipient> membersPreview = recipients.stream().filter(r -> !r.isSelf()).limit(MAX_MEMBER_NAMES).collect(Collectors.toList());
75+
DecryptedGroup decryptedGroup = groupRecord.get().requireV2GroupProperties().getDecryptedGroup();
7776

78-
DecryptedGroup decryptedGroup = groupRecord.get().requireV2GroupProperties().getDecryptedGroup();
79-
groupInfo = new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description, groupHasExistingContacts);
77+
groupInfo = new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description, groupHasExistingContacts, membersPreview);
8078
} else {
81-
groupInfo = new GroupInfo(groupRecord.get().getMembers().size(), 0, "", false);
79+
List<Recipient> membersPreview = recipients.stream().filter(r -> !r.isSelf()).limit(MAX_MEMBER_NAMES).collect(Collectors.toList());
80+
81+
groupInfo = new GroupInfo(groupRecord.get().getMembers().size(), 0, "", false, membersPreview);
8282
}
8383
}
8484

app/src/main/res/layout/conversation_header_view.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,14 @@
9797

9898
<View
9999
android:id="@+id/message_request_divider"
100-
android:layout_width="match_parent"
100+
android:layout_width="0dp"
101101
android:layout_height="1dp"
102102
android:layout_marginTop="16dp"
103+
android:foregroundGravity="center"
104+
android:layout_marginHorizontal="32dp"
103105
android:background="@color/signal_dark_colorTransparentInverse2"
106+
app:layout_constraintStart_toStartOf="parent"
107+
app:layout_constraintEnd_toEndOf="parent"
104108
app:layout_constraintTop_toBottomOf="@id/message_request_about" />
105109

106110
<LinearLayout

app/src/main/res/layout/conversation_item_thread_header.xml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@
22
~ Copyright 2023 Signal Messenger, LLC
33
~ SPDX-License-Identifier: AGPL-3.0-only
44
-->
5-
<org.thoughtcrime.securesms.conversation.ConversationHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
5+
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
66
xmlns:app="http://schemas.android.com/apk/res-auto"
77
xmlns:tools="http://schemas.android.com/tools"
88
android:layout_width="match_parent"
9-
android:layout_height="wrap_content"
10-
android:paddingTop="22dp"
11-
android:paddingBottom="4dp"
12-
app:layout_constraintTop_toTopOf="parent"
13-
tools:viewBindingIgnore="true" />
9+
android:layout_height="wrap_content">
10+
11+
<org.thoughtcrime.securesms.conversation.ConversationHeaderView
12+
android:id="@+id/header"
13+
android:layout_gravity="center_horizontal"
14+
android:layout_width="wrap_content"
15+
android:layout_height="wrap_content"
16+
android:layout_marginTop="32dp"
17+
android:paddingTop="22dp"
18+
android:paddingBottom="4dp"
19+
app:layout_constraintTop_toTopOf="parent"
20+
tools:viewBindingIgnore="true" />
21+
22+
</FrameLayout>

app/src/main/res/values/strings.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,6 +2139,27 @@
21392139
<item quantity="one">%d additional group</item>
21402140
<item quantity="other">%d additional groups</item>
21412141
</plurals>
2142+
<!-- Describes the names of members in a group. MessageRequestProfileView_group_members_* is nested in the first parentheses. MessageRequestProfileView_invited is nested in the second -->
2143+
<string name="MessageRequestProfileView_member_names_and_invited">%1$s (%2$s)</string>
2144+
<!-- Text for an empty group or when you are the only member -->
2145+
<string name="MessageRequestProfileView_group_members_zero">No other group members yet</string>
2146+
<!-- Text for a group with one member (not you). %1$s is their name -->
2147+
<string name="MessageRequestProfileView_group_members_one">%1$s</string>
2148+
<!-- Text for a 2 member group you are in. %1$s is the name of the other member -->
2149+
<string name="MessageRequestProfileView_group_members_one_and_you">%1$s and you</string>
2150+
<!-- Text for a 2 member group you are not in. %1$s and %2$s are the members' names -->
2151+
<string name="MessageRequestProfileView_group_members_two">%1$s and %2$s</string>
2152+
<!-- Text for a 3 member group you are in. %1$s and %2$s are the members' names -->
2153+
<string name="MessageRequestProfileView_group_members_two_and_you">%1$s, %2$s, and you</string>
2154+
<!-- Text for a 3 member group you are not in. %1$s, %2$s, %3$s are the names of the other members. -->
2155+
<string name="MessageRequestProfileView_group_members_three">%1$s, %2$s, and %3$s</string>
2156+
<!-- Text for a 3+ member group. %1$s, %2$s, %3$s are member names. %4$s is the string key MessageRequestProfileView_other_members-->
2157+
<string name="MessageRequestProfileView_group_members_other">%1$s, %2$s, %3$s, and %4$s</string>
2158+
<!-- Nested in MessageRequestProfileView_group_members_other that shows how many members (besides three) are in the group -->
2159+
<plurals name="MessageRequestProfileView_other_members">
2160+
<item quantity="one">%1$d other</item>
2161+
<item quantity="other">%1$d others</item>
2162+
</plurals>
21422163
<!-- Button label to report spam for a conversation when in a message request state -->
21432164
<string name="MessageRequestBottomView_report">Report…</string>
21442165
<!-- Alert dialog title to accept a message request -->

0 commit comments

Comments
 (0)