From f890ae8ddc6a328d62dfd30e2a59f1cddd48f33b Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 19 May 2021 11:57:53 -0400 Subject: [PATCH] Enforce two line limit on group description. Sorry. --- .../components/emoji/EmojiTextView.java | 31 +++++++++---------- .../conversation/ConversationBannerView.java | 7 ++++- .../conversation/ConversationFragment.java | 16 +++++----- .../GroupJoinBottomSheetDialogFragment.java | 12 ++++--- .../ui/managegroup/ManageGroupFragment.java | 10 +++--- .../dialogs/GroupDescriptionDialog.java | 7 +++-- .../groups/v2/GroupDescriptionUtil.java | 27 +++++++++------- .../stickers/StickerManagementAdapter.java | 14 ++++----- .../res/layout/group_join_bottom_sheet.xml | 2 ++ .../main/res/layout/group_manage_fragment.xml | 4 ++- app/src/main/res/values/strings.xml | 2 +- 11 files changed, 75 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java index 78cdd6aab8..8449ea88d9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java @@ -24,7 +24,6 @@ import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; import org.thoughtcrime.securesms.components.mention.MentionAnnotation; import org.thoughtcrime.securesms.components.mention.MentionRendererDelegate; import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; @@ -91,7 +90,8 @@ public class EmojiTextView extends AppCompatTextView { super.onDraw(canvas); } - @Override public void setText(@Nullable CharSequence text, BufferType type) { + @Override + public void setText(@Nullable CharSequence text, BufferType type) { EmojiParser.CandidateList candidates = isInEditMode() ? null : EmojiProvider.getCandidates(text); if (scaleEmojis && candidates != null && candidates.allEmojis) { @@ -118,23 +118,19 @@ public class EmojiTextView extends AppCompatTextView { useSystemEmoji = useSystemEmoji(); if (useSystemEmoji || candidates == null || candidates.size() == 0) { - super.setText(new SpannableStringBuilder(Optional.fromNullable(text).or("")).append(Optional.fromNullable(overflowText).or("")), BufferType.NORMAL); - - if (getEllipsize() == TextUtils.TruncateAt.END && maxLength > 0) { - ellipsizeAnyTextForMaxLength(); - } + super.setText(new SpannableStringBuilder(Optional.fromNullable(text).or("")), BufferType.NORMAL); } else { CharSequence emojified = EmojiProvider.emojify(candidates, text, this); - super.setText(new SpannableStringBuilder(emojified).append(Optional.fromNullable(overflowText).or("")), BufferType.SPANNABLE); + super.setText(new SpannableStringBuilder(emojified), BufferType.SPANNABLE); + } - // Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688) - // We ellipsize them ourselves by manually truncating the appropriate section. - if (getEllipsize() == TextUtils.TruncateAt.END) { - if (maxLength > 0) { - ellipsizeAnyTextForMaxLength(); - } else { - ellipsizeEmojiTextForMaxLines(); - } + // Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688) + // We ellipsize them ourselves by manually truncating the appropriate section. + if (getText() != null && getText().length() > 0 && getEllipsize() == TextUtils.TruncateAt.END) { + if (maxLength > 0) { + ellipsizeAnyTextForMaxLength(); + } else if (getMaxLines() > 0) { + ellipsizeEmojiTextForMaxLines(); } } @@ -192,7 +188,8 @@ public class EmojiTextView extends AppCompatTextView { if (lineCount > maxLines) { int overflowStart = getLayout().getLineStart(maxLines - 1); CharSequence overflow = getText().subSequence(overflowStart, getText().length()); - CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth(), TextUtils.TruncateAt.END); + float adjust = overflowText != null ? getPaint().measureText(overflowText, 0, overflowText.length()) : 0f; + CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth() - adjust, TextUtils.TruncateAt.END); SpannableStringBuilder newContent = new SpannableStringBuilder(); newContent.append(getText().subSequence(0, overflowStart)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java index 6f6ff36efd..7d4562160f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationBannerView.java @@ -19,6 +19,7 @@ import androidx.core.widget.ImageViewCompat; import org.signal.core.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.R; 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.DatabaseFactory; @@ -31,7 +32,7 @@ public class ConversationBannerView extends ConstraintLayout { private TextView contactTitle; private TextView contactAbout; private TextView contactSubtitle; - private TextView contactDescription; + private EmojiTextView contactDescription; private View tapToView; public ConversationBannerView(Context context) { @@ -91,6 +92,10 @@ public class ConversationBannerView extends ConstraintLayout { contactDescription.setVisibility(TextUtils.isEmpty(description) ? GONE : VISIBLE); } + public @NonNull EmojiTextView getDescription() { + return contactDescription; + } + public void showBackgroundBubble(boolean enabled) { if (enabled) { setBackgroundResource(R.drawable.wallpaper_bubble_background_12); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index f8e25c7aba..a24ad20338 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -541,13 +541,15 @@ public class ConversationFragment extends LoggingFragment { } else { conversationBanner.setLinkifyDescription(true); boolean linkifyWebLinks = recipientInfo.getMessageRequestState() == MessageRequestState.NONE; - conversationBanner.setDescription(GroupDescriptionUtil.style(context, - recipientInfo.getGroupDescription(), - linkifyWebLinks, - () -> GroupDescriptionDialog.show(getChildFragmentManager(), - recipient.getDisplayName(context), - recipientInfo.getGroupDescription(), - linkifyWebLinks))); + conversationBanner.showDescription(); + GroupDescriptionUtil.setText(context, + conversationBanner.getDescription(), + recipientInfo.getGroupDescription(), + linkifyWebLinks, + () -> GroupDescriptionDialog.show(getChildFragmentManager(), + recipient.getDisplayName(context), + recipientInfo.getGroupDescription(), + linkifyWebLinks)); } } else { final String description; diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java index e23ded8be9..90df28c487 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java @@ -24,6 +24,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.color.MaterialColor; 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.conversation.ConversationIntents; @@ -46,7 +47,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF private AvatarImageView avatar; private TextView groupName; private TextView groupDetails; - private TextView groupDescription; + private EmojiTextView groupDescription; private TextView groupJoinExplain; private Button groupJoinButton; private Button groupCancelButton; @@ -158,10 +159,11 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF private void updateGroupDescription(@NonNull String name, @NonNull String description) { groupDescription.setVisibility(View.VISIBLE); groupDescription.setMovementMethod(LinkMovementMethod.getInstance()); - groupDescription.setText(GroupDescriptionUtil.style(requireContext(), - description, - true, - () -> GroupDescriptionDialog.show(getChildFragmentManager(), name, description, true))); + GroupDescriptionUtil.setText(requireContext(), + groupDescription, + description, + true, + () -> GroupDescriptionDialog.show(getChildFragmentManager(), name, description, true)); } private static ExtendedGroupJoinStatus getGroupJoinStatus() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java index 0c4cc64804..a4b018e019 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java @@ -458,15 +458,17 @@ public class ManageGroupFragment extends LoggingFragment { if (TextUtils.isEmpty(description.getDescription())) { if (FeatureFlags.groupsV2Description() && description.canEditDescription()) { + groupDescription.setOverflowText(null); groupDescription.setText(R.string.ManageGroupActivity_add_group_description); groupDescription.setOnClickListener(v -> startActivity(EditProfileActivity.getIntentForGroupProfile(requireActivity(), getGroupId()))); } } else { groupDescription.setOnClickListener(null); - groupDescription.setText(GroupDescriptionUtil.style(requireContext(), - description.getDescription(), - description.shouldLinkifyWebLinks(), - () -> GroupDescriptionDialog.show(getChildFragmentManager(), getGroupId(), null, description.shouldLinkifyWebLinks()))); + GroupDescriptionUtil.setText(requireContext(), + groupDescription, + description.getDescription(), + description.shouldLinkifyWebLinks(), + () -> GroupDescriptionDialog.show(getChildFragmentManager(), getGroupId(), null, description.shouldLinkifyWebLinks())); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupDescriptionDialog.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupDescriptionDialog.java index a72fc16f86..5ec6251818 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupDescriptionDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupDescriptionDialog.java @@ -16,6 +16,7 @@ import androidx.fragment.app.FragmentManager; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.LiveGroup; import org.thoughtcrime.securesms.groups.ParcelableGroupId; @@ -34,7 +35,7 @@ public final class GroupDescriptionDialog extends DialogFragment { private static final String ARGUMENT_LINKIFY = "linkify"; private static final String DIALOG_TAG = "GroupDescriptionDialog"; - private TextView descriptionText; + private EmojiTextView descriptionText; public static void show(@NonNull FragmentManager fragmentManager, @NonNull String title, @Nullable String description, boolean linkify) { show(fragmentManager, null, title, description, linkify); @@ -76,9 +77,9 @@ public final class GroupDescriptionDialog extends DialogFragment { .create(); if (argumentDescription != null) { - descriptionText.setText(GroupDescriptionUtil.style(requireContext(), argumentDescription, linkify, null)); + GroupDescriptionUtil.setText(requireContext(), descriptionText, argumentDescription, linkify, null); } else if (liveGroup != null) { - liveGroup.getDescription().observe(this, d -> descriptionText.setText(GroupDescriptionUtil.style(requireContext(), d, linkify, null))); + liveGroup.getDescription().observe(this, d -> GroupDescriptionUtil.setText(requireContext(), descriptionText, d, linkify, null)); } if (TextUtils.isEmpty(argumentTitle) && liveGroup != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/GroupDescriptionUtil.java b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/GroupDescriptionUtil.java index 74a1820033..03a3b53e61 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/v2/GroupDescriptionUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/v2/GroupDescriptionUtil.java @@ -7,6 +7,7 @@ import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; +import android.text.TextUtils; import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.text.util.Linkify; @@ -18,6 +19,7 @@ import androidx.annotation.Nullable; import com.annimon.stream.Stream; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; public final class GroupDescriptionUtil { @@ -25,14 +27,14 @@ public final class GroupDescriptionUtil { public static final int MAX_DESCRIPTION_LENGTH = 80; /** - * Style a group description. + * Set a group description. * - * @param description full description - * @param linkify flag indicating if web urls should be linkified - * @param moreClick Callback for when truncating and need to show more via another means. Required to enable truncating. - * @return styled group description + * @param description full description + * @param emojiTextView Text view to update with description + * @param linkify flag indicating if web urls should be linkified + * @param moreClick Callback for when truncating and need to show more via another means. Required to enable truncating. */ - public static @NonNull Spannable style(@NonNull Context context, @NonNull String description, boolean linkify, @Nullable Runnable moreClick) { + public static void setText(@NonNull Context context, @NonNull EmojiTextView emojiTextView, @NonNull String description, boolean linkify, @Nullable Runnable moreClick) { SpannableString descriptionSpannable = new SpannableString(description); if (linkify) { @@ -46,7 +48,7 @@ public final class GroupDescriptionUtil { } } - if (moreClick != null && descriptionSpannable.length() > MAX_DESCRIPTION_LENGTH) { + if (moreClick != null) { ClickableSpan style = new ClickableSpan() { @Override public void onClick(@NonNull View widget) { @@ -59,11 +61,14 @@ public final class GroupDescriptionUtil { } }; - SpannableStringBuilder builder = new SpannableStringBuilder(descriptionSpannable.subSequence(0, MAX_DESCRIPTION_LENGTH)).append(context.getString(R.string.ManageGroupActivity_more)); - builder.setSpan(style, MAX_DESCRIPTION_LENGTH + 1, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - return builder; + emojiTextView.setEllipsize(TextUtils.TruncateAt.END); + emojiTextView.setMaxLines(2); + + SpannableString overflowText = new SpannableString(context.getString(R.string.ManageGroupActivity_more)); + overflowText.setSpan(style, 0, overflowText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + emojiTextView.setOverflowText(overflowText); } - return descriptionSpannable; + emojiTextView.setText(descriptionSpannable); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java index 6773910a80..4a1417f67d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java @@ -4,6 +4,7 @@ import android.content.Context; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; import android.view.LayoutInflater; import android.view.View; @@ -261,16 +262,15 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapterupgrade this group. This is an insecure MMS Group. To chat privately, invite your contacts to Signal. Invite now - …more + more Add group description…