Update group terminated banner.

This commit is contained in:
Cody Henthorne
2026-03-25 15:14:05 -04:00
parent 206f6d84e7
commit 2b9126d74b
11 changed files with 61 additions and 88 deletions

View File

@@ -73,7 +73,6 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.L
import org.thoughtcrime.securesms.components.settings.conversation.preferences.LegacyGroupPreference
import org.thoughtcrime.securesms.components.settings.conversation.preferences.RecipientPreference
import org.thoughtcrime.securesms.components.settings.conversation.preferences.SharedMediaPreference
import org.thoughtcrime.securesms.components.settings.conversation.preferences.TerminatedBannerPreference
import org.thoughtcrime.securesms.components.settings.conversation.preferences.Utils.formatMutedUntil
import org.thoughtcrime.securesms.conversation.ConversationIntents
import org.thoughtcrime.securesms.conversation.colors.ColorizerV2
@@ -306,7 +305,6 @@ class ConversationSettingsFragment :
InternalPreference.register(adapter)
GroupDescriptionPreference.register(adapter)
LegacyGroupPreference.register(adapter)
TerminatedBannerPreference.register(adapter)
CallPreference.register(adapter)
val recipientId = args.recipientId
@@ -369,17 +367,10 @@ class ConversationSettingsFragment :
return@configure
}
state.withGroupSettingsState {
if (it.isTerminated) {
customPref(TerminatedBannerPreference.Model())
}
}
customPref(
AvatarPreference.Model(
recipient = state.recipient,
storyViewState = state.storyViewState,
reduceTopMargin = state.isTerminatedGroup,
onAvatarClick = { avatar ->
val viewAvatarIntent = AvatarPreviewActivity.intentFromRecipientId(requireContext(), state.recipient.id)
val viewAvatarTransitionBundle = AvatarPreviewActivity.createTransitionBundle(requireActivity(), avatar)
@@ -434,7 +425,8 @@ class ConversationSettingsFragment :
customPref(
BioTextPreference.GroupModel(
groupTitle = groupState.groupTitle,
groupMembershipDescription = groupMembershipDescription
groupMembershipDescription = groupMembershipDescription,
isTerminated = groupState.isTerminated
)
)

View File

@@ -1,9 +1,7 @@
package org.thoughtcrime.securesms.components.settings.conversation.preferences
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import org.signal.core.util.dp
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.view.AvatarView
import org.thoughtcrime.securesms.badges.BadgeImageView
@@ -27,7 +25,6 @@ object AvatarPreference {
class Model(
val recipient: Recipient,
val storyViewState: StoryViewState,
val reduceTopMargin: Boolean = false,
val onAvatarClick: (AvatarView) -> Unit,
val onBadgeClick: (Badge) -> Unit
) : PreferenceModel<Model>() {
@@ -38,8 +35,7 @@ object AvatarPreference {
override fun areContentsTheSame(newItem: Model): Boolean {
return super.areContentsTheSame(newItem) &&
recipient.hasSameContent(newItem.recipient) &&
storyViewState == newItem.storyViewState &&
reduceTopMargin == newItem.reduceTopMargin
storyViewState == newItem.storyViewState
}
}
@@ -53,10 +49,6 @@ object AvatarPreference {
}
override fun bind(model: Model) {
(itemView.layoutParams as? ViewGroup.MarginLayoutParams)?.let {
it.topMargin = if (model.reduceTopMargin) 0.dp else 40.dp
}
if (model.recipient.isSelf) {
badge.setBadge(null)
badge.setOnClickListener(null)

View File

@@ -2,11 +2,13 @@ package org.thoughtcrime.securesms.components.settings.conversation.preferences
import android.content.ClipData
import android.content.Context
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.TextView
import android.widget.Toast
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.fonts.SignalSymbols
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
@@ -61,7 +63,8 @@ object BioTextPreference {
class GroupModel(
val groupTitle: String,
val groupMembershipDescription: String?
val groupMembershipDescription: String?,
val isTerminated: Boolean = false
) : BioTextPreferenceModel<GroupModel>() {
override fun getHeadlineText(context: Context): CharSequence = groupTitle
@@ -72,7 +75,8 @@ object BioTextPreference {
override fun areContentsTheSame(newItem: GroupModel): Boolean {
return super.areContentsTheSame(newItem) &&
groupTitle == newItem.groupTitle &&
groupMembershipDescription == newItem.groupMembershipDescription
groupMembershipDescription == newItem.groupMembershipDescription &&
isTerminated == newItem.isTerminated
}
override fun areItemsTheSame(newItem: GroupModel): Boolean {
@@ -85,6 +89,7 @@ object BioTextPreference {
private val headline: TextView = itemView.findViewById(R.id.bio_preference_headline)
private val subhead1: TextView = itemView.findViewById(R.id.bio_preference_subhead_1)
protected val subhead2: TextView = itemView.findViewById(R.id.bio_preference_subhead_2)
private val terminatedPill: TextView = itemView.findViewById(R.id.bio_preference_terminated_pill)
override fun bind(model: T) {
headline.text = model.getHeadlineText(context)
@@ -94,6 +99,17 @@ object BioTextPreference {
headline.setOnClickListener { clickListener() }
}
if (model is GroupModel && model.isTerminated) {
val glyphSpan = SignalSymbols.getSpannedString(context, SignalSymbols.Weight.REGULAR, SignalSymbols.Glyph.GROUP_X)
terminatedPill.text = SpannableStringBuilder()
.append(glyphSpan)
.append(" ")
.append(context.getString(R.string.ConversationSettingsFragment__this_group_was_ended))
terminatedPill.visibility = View.VISIBLE
} else {
terminatedPill.visibility = View.GONE
}
model.getSubhead1Text(context).let {
subhead1.text = it
subhead1.visibility = if (it == null) View.GONE else View.VISIBLE

View File

@@ -1,29 +0,0 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.components.settings.conversation.preferences
import android.view.View
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
object TerminatedBannerPreference {
fun register(adapter: MappingAdapter) {
adapter.registerFactory(Model::class.java, LayoutFactory(::ViewHolder, R.layout.conversation_settings_terminated_banner))
}
class Model : PreferenceModel<Model>() {
override fun areItemsTheSame(newItem: Model): Boolean = true
override fun areContentsTheSame(newItem: Model): Boolean = true
}
private class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
override fun bind(model: Model) = Unit
}
}

View File

@@ -3675,11 +3675,18 @@ class ConversationFragment :
}
override fun onBlockJoinRequest(recipient: Recipient) {
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.ConversationFragment__block_request)
.setMessage(getString(R.string.ConversationFragment__s_will_not_be_able_to_join_or_request_to_join_this_group_via_the_group_link, recipient.getDisplayName(requireContext())))
.setNegativeButton(R.string.ConversationFragment__cancel, null)
.setPositiveButton(R.string.ConversationFragment__block_request_button) { _, _ -> handleBlockJoinRequest(recipient) }
.show()
if (conversationGroupViewModel.groupRecordSnapshot?.isTerminated == true) {
MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.conversation_activity__group_action_not_allowed_group_ended)
.setPositiveButton(android.R.string.ok) { d, _ -> d.dismiss() }
.show()
} else {
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.ConversationFragment__block_request)
.setMessage(getString(R.string.ConversationFragment__s_will_not_be_able_to_join_or_request_to_join_this_group_via_the_group_link, recipient.getDisplayName(requireContext())))
.setNegativeButton(R.string.ConversationFragment__cancel, null)
.setPositiveButton(R.string.ConversationFragment__block_request_button) { _, _ -> handleBlockJoinRequest(recipient) }
.show()
}
}
override fun onRecipientNameClicked(target: RecipientId) {

View File

@@ -236,12 +236,12 @@ final class GroupsV2UpdateMessageProducer {
private void describeGroupTerminateUpdate(@NonNull GroupTerminateChangeUpdate update, @NonNull List<UpdateDescription> updates) {
if (update.updaterAci == null) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_was_terminated), Glyph.X_CIRCLE));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_was_terminated), Glyph.GROUP_X));
} else {
if (selfIds.matches(update.updaterAci)) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_terminated_the_group), Glyph.X_CIRCLE));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_terminated_the_group), Glyph.GROUP_X));
} else {
updates.add(updateDescription(R.string.MessageRecord_s_terminated_the_group, update.updaterAci, Glyph.X_CIRCLE));
updates.add(updateDescription(R.string.MessageRecord_s_terminated_the_group, update.updaterAci, Glyph.GROUP_X));
}
}
}

View File

@@ -113,6 +113,7 @@ object SignalSymbols {
GIF('\uE037'),
GIF_RECTANGLE('\uE097'),
GROUP('\uE038'),
GROUP_X('\uE0AE'),
HEART('\uE039'),
INCOMING('\uE03A'),
INFO('\uE03B'),

View File

@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="12dp" />
<stroke
android:width="1dp"
android:color="@color/signal_colorOutline_38" />
<corners android:radius="8dp" />
<solid android:color="@color/signal_colorSurfaceVariant" />
</shape>

View File

@@ -20,6 +20,26 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="Miles Morales" />
<TextView
android:id="@+id/bio_preference_terminated_pill"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/terminated_pill_background"
android:gravity="center_vertical"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
android:paddingBottom="6dp"
android:textAppearance="@style/Signal.Text.BodyMedium"
android:textColor="@color/signal_colorOnSurface"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bio_preference_headline"
tools:text="This group was ended."
tools:visibility="visible" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/bio_preference_subhead_1"
android:layout_width="0dp"
@@ -33,7 +53,7 @@
android:textColor="@color/signal_text_secondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bio_preference_headline"
app:layout_constraintTop_toBottomOf="@id/bio_preference_terminated_pill"
tools:text=":-) Just hanging around." />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/terminated_banner_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/terminated_banner_background"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@string/ConversationSettingsFragment__this_group_was_ended"
android:textAppearance="@style/Signal.Text.BodyMedium"
android:textColor="@color/signal_colorOnSurface" />
</FrameLayout>

View File

@@ -3640,7 +3640,7 @@
<!-- Dialog body shown when tapping a failed message in a terminated group -->
<string name="conversation_activity__send_failed_group_ended">Send failed because the group was ended. You can no longer send and receive messages in this group.</string>
<!-- Dialog body shown when tapping a group action button (e.g. invite friends) in a terminated group -->
<string name="conversation_activity__group_action_not_allowed_group_ended">You can no longer invite friends or add members to this group.</string>
<string name="conversation_activity__group_action_not_allowed_group_ended">This action is unavailable because the group has ended.</string>
<!-- Dialog body when a message failed to delete and retry is possible. -->
<string name="conversation_activity__message_failed_to_delete_retry">Message failed to delete. Check your connection and try again.</string>
<!-- Dialog body when a message failed to delete. -->