diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java index 328be627b5..6f7b0a5f56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java @@ -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 { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt index 07e1e14b35..34f07c2199 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt @@ -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() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java index 37873dc22d..44dd314459 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java @@ -2,7 +2,9 @@ package org.thoughtcrime.securesms.util; import android.content.Context; import android.content.res.Resources; +import android.graphics.Color; import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; @@ -33,6 +35,7 @@ import androidx.annotation.StringRes; import androidx.annotation.StyleRes; import androidx.core.content.ContextCompat; +import org.signal.core.util.DimensionUnit; import org.thoughtcrime.securesms.R; public final class SpanUtil { @@ -128,6 +131,13 @@ public final class SpanUtil { return imageSpan; } + public static CharSequence space(int width, @NonNull DimensionUnit widthUnit) { + Drawable drawable = new ColorDrawable(Color.TRANSPARENT); + drawable.setBounds(0, 0, (int) widthUnit.toPixels(width), 1); + + return buildCenteredImageSpan(drawable); + } + public static void appendCenteredImageSpan(@NonNull SpannableStringBuilder builder, @NonNull Drawable drawable, int width, int height) { drawable.setBounds(0, 0, ViewUtil.dpToPx(width), ViewUtil.dpToPx(height)); builder.append(" ").append(SpanUtil.buildCenteredImageSpan(drawable)); diff --git a/app/src/main/res/drawable/message_request_info_outline.xml b/app/src/main/res/drawable/message_request_info_outline.xml new file mode 100644 index 0000000000..8ca7527dc0 --- /dev/null +++ b/app/src/main/res/drawable/message_request_info_outline.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/symbol_group_light_20.xml b/app/src/main/res/drawable/symbol_group_light_20.xml new file mode 100644 index 0000000000..5a45ae198e --- /dev/null +++ b/app/src/main/res/drawable/symbol_group_light_20.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/symbol_phone_light_20.xml b/app/src/main/res/drawable/symbol_phone_light_20.xml new file mode 100644 index 0000000000..05f8214962 --- /dev/null +++ b/app/src/main/res/drawable/symbol_phone_light_20.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/conversation_banner_view.xml b/app/src/main/res/layout/conversation_header_view.xml similarity index 57% rename from app/src/main/res/layout/conversation_banner_view.xml rename to app/src/main/res/layout/conversation_header_view.xml index 416e8c8176..a049d3e0b1 100644 --- a/app/src/main/res/layout/conversation_banner_view.xml +++ b/app/src/main/res/layout/conversation_header_view.xml @@ -1,17 +1,16 @@ - + - + + + app:layout_constraintTop_toBottomOf="@id/message_request_divider" + app:layout_constraintWidth_max="308dp" + app:layout_constraintWidth_min="256dp"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_item_thread_header.xml b/app/src/main/res/layout/conversation_item_thread_header.xml index b60b16cb05..2630d18ab1 100644 --- a/app/src/main/res/layout/conversation_item_thread_header.xml +++ b/app/src/main/res/layout/conversation_item_thread_header.xml @@ -1,18 +1,14 @@ - - - + android:layout_marginHorizontal="52dp" + android:paddingTop="22dp" + android:paddingBottom="4dp" + app:layout_constraintTop_toTopOf="parent" + tools:viewBindingIgnore="true" />