Update style for conversation header view.

This commit is contained in:
Alex Hart
2023-12-21 12:08:35 -04:00
committed by Clark Chen
parent 2b606a2dec
commit d70ebc2398
8 changed files with 174 additions and 86 deletions

View File

@@ -1,39 +1,36 @@
package org.thoughtcrime.securesms.conversation;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import org.signal.core.util.DimensionUnit;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.badges.BadgeImageView;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.databinding.ConversationHeaderViewBinding;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ContextUtil;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.whispersystems.signalservice.api.util.Preconditions;
public class ConversationHeaderView extends ConstraintLayout {
private AvatarImageView contactAvatar;
private TextView contactTitle;
private TextView contactAbout;
private TextView contactSubtitle;
private EmojiTextView contactDescription;
private View tapToView;
private BadgeImageView contactBadge;
private final ConversationHeaderViewBinding binding;
public ConversationHeaderView(Context context) {
this(context, null);
@@ -46,38 +43,32 @@ public class ConversationHeaderView extends ConstraintLayout {
public ConversationHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(getContext(), R.layout.conversation_banner_view, this);
inflate(getContext(), R.layout.conversation_header_view, this);
contactAvatar = findViewById(R.id.message_request_avatar);
contactBadge = findViewById(R.id.message_request_badge);
contactTitle = findViewById(R.id.message_request_title);
contactAbout = findViewById(R.id.message_request_about);
contactSubtitle = findViewById(R.id.message_request_subtitle);
contactDescription = findViewById(R.id.message_request_description);
tapToView = findViewById(R.id.message_request_avatar_tap_to_view);
binding = ConversationHeaderViewBinding.bind(this);
contactAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
binding.messageRequestAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
}
public void setBadge(@Nullable Recipient recipient) {
if (recipient == null || recipient.isSelf()) {
contactBadge.setBadge(null);
binding.messageRequestBadge.setBadge(null);
} else {
contactBadge.setBadgeFromRecipient(recipient);
binding.messageRequestBadge.setBadgeFromRecipient(recipient);
}
}
public void setAvatar(@NonNull GlideRequests requests, @Nullable Recipient recipient) {
contactAvatar.setAvatar(requests, recipient, false);
binding.messageRequestAvatar.setAvatar(requests, recipient, false);
if (recipient != null && recipient.shouldBlurAvatar() && recipient.getContactPhoto() != null) {
tapToView.setVisibility(VISIBLE);
tapToView.setOnClickListener(v -> {
binding.messageRequestAvatarTapToView.setVisibility(VISIBLE);
binding.messageRequestAvatarTapToView.setOnClickListener(v -> {
SignalExecutors.BOUNDED.execute(() -> SignalDatabase.recipients().manuallyShowAvatar(recipient.getId()));
});
} else {
tapToView.setVisibility(GONE);
tapToView.setOnClickListener(null);
binding.messageRequestAvatarTapToView.setVisibility(GONE);
binding.messageRequestAvatarTapToView.setOnClickListener(null);
}
}
@@ -86,7 +77,7 @@ public class ConversationHeaderView extends ConstraintLayout {
if (recipient.showVerified()) {
SpanUtil.appendCenteredImageSpan(title, ContextUtil.requireDrawable(getContext(), R.drawable.ic_official_28), 28, 28);
}
contactTitle.setText(title);
binding.messageRequestTitle.setText(title);
return title.toString();
}
@@ -98,46 +89,66 @@ public class ConversationHeaderView extends ConstraintLayout {
about = recipient.getCombinedAboutAndEmoji();
}
contactAbout.setText(about);
contactAbout.setVisibility(TextUtils.isEmpty(about) ? GONE : VISIBLE);
binding.messageRequestAbout.setText(about);
binding.messageRequestAbout.setVisibility(TextUtils.isEmpty(about) ? GONE : VISIBLE);
}
public void setSubtitle(@Nullable CharSequence subtitle) {
contactSubtitle.setText(subtitle);
contactSubtitle.setVisibility(TextUtils.isEmpty(subtitle) ? GONE : VISIBLE);
public void setSubtitle(@NonNull CharSequence subtitle, @DrawableRes int iconRes) {
binding.messageRequestSubtitle.setText(prependIcon(subtitle, iconRes));
binding.messageRequestSubtitle.setVisibility(View.VISIBLE);
}
public void setDescription(@Nullable CharSequence description) {
contactDescription.setText(description);
contactDescription.setVisibility(TextUtils.isEmpty(description) ? GONE : VISIBLE);
public void setDescription(@Nullable CharSequence description, @DrawableRes int iconRes) {
if (description == null) {
hideDescription();
return;
}
binding.messageRequestSubtitle.setText(prependIcon(description, iconRes));
binding.messageRequestDescription.setVisibility(View.VISIBLE);
}
public @NonNull EmojiTextView getDescription() {
return contactDescription;
return binding.messageRequestDescription;
}
public void showBackgroundBubble(boolean enabled) {
if (enabled) {
setBackgroundResource(R.drawable.wallpaper_bubble_background_12);
setBackgroundResource(R.drawable.wallpaper_bubble_background_18);
binding.messageRequestInfoOutline.setVisibility(View.INVISIBLE);
binding.messageRequestDivider.setVisibility(View.VISIBLE);
} else {
setBackground(null);
binding.messageRequestInfoOutline.setVisibility(View.VISIBLE);
binding.messageRequestDivider.setVisibility(View.INVISIBLE);
}
}
public void hideSubtitle() {
contactSubtitle.setVisibility(View.GONE);
binding.messageRequestSubtitle.setVisibility(View.GONE);
}
public void showDescription() {
contactDescription.setVisibility(View.VISIBLE);
binding.messageRequestDescription.setVisibility(View.VISIBLE);
}
public void hideDescription() {
contactDescription.setVisibility(View.GONE);
binding.messageRequestDescription.setVisibility(View.GONE);
}
public void setLinkifyDescription(boolean enable) {
contactDescription.setMovementMethod(enable ? LongClickMovementMethod.getInstance(getContext()) : null);
binding.messageRequestDescription.setMovementMethod(enable ? LongClickMovementMethod.getInstance(getContext()) : null);
}
private @NonNull CharSequence prependIcon(@NonNull CharSequence input, @DrawableRes int iconRes) {
Drawable drawable = ContextCompat.getDrawable(getContext(), iconRes);
Preconditions.checkNotNull(drawable);
drawable.setBounds(0, 0, (int) DimensionUnit.DP.toPixels(20), (int) DimensionUnit.DP.toPixels(20));
return new SpannableStringBuilder()
.append(SpanUtil.buildCenteredImageSpan(drawable))
.append(SpanUtil.space(8, DimensionUnit.DP))
.append(input);
}
private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider {

View File

@@ -53,7 +53,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.CachedInflater
import org.thoughtcrime.securesms.util.HtmlUtil
import org.thoughtcrime.securesms.util.Projection
import org.thoughtcrime.securesms.util.ProjectionList
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
@@ -572,20 +571,20 @@ class ConversationAdapterV2(
if (recipient.isGroup) {
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))
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members_and_invited, groupInfo.fullMemberCount, groupInfo.fullMemberCount, invited), R.drawable.symbol_group_light_20)
} else if (groupInfo.fullMemberCount > 0) {
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members, groupInfo.fullMemberCount, groupInfo.fullMemberCount))
conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members, groupInfo.fullMemberCount, groupInfo.fullMemberCount), R.drawable.symbol_group_light_20)
} else {
conversationBanner.setSubtitle(null)
conversationBanner.hideSubtitle()
}
} else if (isSelf) {
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation))
conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation), R.drawable.symbol_person_light_24)
} else {
val subtitle: String? = recipient.takeIf { it.shouldShowE164() }?.e164?.map { e164: String? -> PhoneNumberFormatter.prettyPrint(e164!!) }?.orElse(null)
if (subtitle == null || subtitle == title) {
conversationBanner.hideSubtitle()
} else {
conversationBanner.setSubtitle(subtitle)
conversationBanner.setSubtitle(subtitle, R.drawable.symbol_phone_light_20)
}
}
@@ -609,20 +608,20 @@ class ConversationAdapterV2(
}
} else {
val description: String = when (sharedGroups.size) {
1 -> context.getString(R.string.MessageRequestProfileView_member_of_one_group, HtmlUtil.bold(sharedGroups[0]))
2 -> context.getString(R.string.MessageRequestProfileView_member_of_two_groups, HtmlUtil.bold(sharedGroups[0]), HtmlUtil.bold(sharedGroups[1]))
3 -> context.getString(R.string.MessageRequestProfileView_member_of_many_groups, HtmlUtil.bold(sharedGroups[0]), HtmlUtil.bold(sharedGroups[1]), HtmlUtil.bold(sharedGroups[2]))
1 -> context.getString(R.string.MessageRequestProfileView_member_of_one_group, sharedGroups[0])
2 -> context.getString(R.string.MessageRequestProfileView_member_of_two_groups, sharedGroups[0], sharedGroups[1])
3 -> context.getString(R.string.MessageRequestProfileView_member_of_many_groups, sharedGroups[0], sharedGroups[1], sharedGroups[2])
else -> {
val others: Int = sharedGroups.size - 2
context.getString(
R.string.MessageRequestProfileView_member_of_many_groups,
HtmlUtil.bold(sharedGroups[0]),
HtmlUtil.bold(sharedGroups[1]),
sharedGroups[0],
sharedGroups[1],
context.resources.getQuantityString(R.plurals.MessageRequestProfileView_member_of_d_additional_groups, others, others)
)
}
}
conversationBanner.setDescription(HtmlCompat.fromHtml(description, 0))
conversationBanner.setDescription(HtmlCompat.fromHtml(description, 0), R.drawable.symbol_group_light_20)
conversationBanner.showDescription()
}
}