Fix multiple issues with rendering spoilers as story captions.

This commit is contained in:
Cody Henthorne 2023-04-25 09:51:11 -04:00 committed by GitHub
parent a837f86999
commit b9d7d19dea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 59 deletions

View file

@ -320,23 +320,7 @@ public class EmojiTextView extends AppCompatTextView {
if (maxLength > 0 && getText().length() > maxLength + 1) {
SpannableStringBuilder newContent = new SpannableStringBuilder();
SpannableString shortenedText = new SpannableString(getText().subSequence(0, maxLength));
List<Annotation> mentionAnnotations = MentionAnnotation.getMentionAnnotations(shortenedText, maxLength - 1, maxLength);
if (!mentionAnnotations.isEmpty()) {
shortenedText = new SpannableString(shortenedText.subSequence(0, shortenedText.getSpanStart(mentionAnnotations.get(0))));
}
Object[] endSpans = shortenedText.getSpans(shortenedText.length() - 1, shortenedText.length(), Object.class);
for (Object span : endSpans) {
if (shortenedText.getSpanFlags(span) == Spanned.SPAN_EXCLUSIVE_INCLUSIVE) {
int start = shortenedText.getSpanStart(span);
int end = shortenedText.getSpanEnd(span);
shortenedText.removeSpan(span);
shortenedText.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
newContent.append(shortenedText)
newContent.append(getText(maxLength))
.append(ELLIPSIS)
.append(Util.emptyIfNull(overflowText));
@ -379,9 +363,12 @@ public class EmojiTextView extends AppCompatTextView {
CharSequence ellipsized = StringUtil.trim(TextUtils.ellipsize(overflow, getPaint(), getWidth() - adjust, TextUtils.TruncateAt.END));
SpannableStringBuilder newContent = new SpannableStringBuilder();
newContent.append(getText().subSequence(0, overflowStart))
.append(ellipsized.subSequence(0, ellipsized.length()))
.append(Optional.ofNullable(overflowText).orElse(""));
newContent.append(getText().subSequence(0, overflowStart).toString())
.append(ellipsized.subSequence(0, ellipsized.length()).toString());
TextUtils.copySpansFrom(getText(newContent.length() - 1), 0, newContent.length() - 1, Object.class, newContent, 0);
newContent.append(Optional.ofNullable(overflowText).orElse(""));
EmojiParser.CandidateList newCandidates = isInEditMode() ? null : EmojiProvider.getCandidates(newContent);
@ -406,6 +393,27 @@ public class EmojiTextView extends AppCompatTextView {
}
}
/** Get text but truncated to maxLength, adjusts for end mentions and converts style spans to be exclusive on start and end. */
private SpannableString getText(int maxLength) {
SpannableString shortenedText = new SpannableString(getText().subSequence(0, maxLength));
List<Annotation> mentionAnnotations = MentionAnnotation.getMentionAnnotations(shortenedText, maxLength - 1, maxLength);
if (!mentionAnnotations.isEmpty()) {
shortenedText = new SpannableString(shortenedText.subSequence(0, shortenedText.getSpanStart(mentionAnnotations.get(0))));
}
Object[] endSpans = shortenedText.getSpans(shortenedText.length() - 1, shortenedText.length(), Object.class);
for (Object span : endSpans) {
if (shortenedText.getSpanFlags(span) == Spanned.SPAN_EXCLUSIVE_INCLUSIVE) {
int start = shortenedText.getSpanStart(span);
int end = shortenedText.getSpanEnd(span);
shortenedText.removeSpan(span);
shortenedText.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return shortenedText;
}
private boolean unchanged(CharSequence text, CharSequence overflowText, BufferType bufferType) {
return Util.equals(previousText, text) &&
Util.equals(previousOverflowText, overflowText) &&

View file

@ -16,6 +16,7 @@ import org.signal.core.util.getParcelableArrayListCompat
import org.signal.core.util.getParcelableCompat
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@ -58,6 +59,8 @@ class StoryViewerFragment :
ViewCompat.setTransitionName(storyCrossfader, "story")
storyCrossfader.callback = this
SpoilerAnnotation.resetRevealedSpoilers()
val adapter = StoryViewerPagerAdapter(
this,
StoryViewerPageArgs(

View file

@ -10,6 +10,7 @@ import android.graphics.drawable.Drawable
import android.media.AudioManager
import android.os.Bundle
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.method.ScrollingMovementMethod
import android.view.GestureDetector
import android.view.MotionEvent
@ -24,7 +25,6 @@ import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.animation.PathInterpolatorCompat
import androidx.core.view.doOnNextLayout
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
@ -42,6 +42,7 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.animation.AnimationCompleteListener
import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
import org.thoughtcrime.securesms.components.segmentedprogressbar.SegmentedProgressBar
import org.thoughtcrime.securesms.components.segmentedprogressbar.SegmentedProgressBarListener
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
@ -177,7 +178,7 @@ class StoryViewerPageFragment :
val distributionList: TextView = view.findViewById(R.id.distribution_list)
val cardWrapper: TouchInterceptingFrameLayout = view.findViewById(R.id.story_content_card_touch_interceptor)
val card: MaterialCardView = view.findViewById(R.id.story_content_card)
val caption: TextView = view.findViewById(R.id.story_caption)
val caption: EmojiTextView = view.findViewById(R.id.story_caption)
val largeCaption: TextView = view.findViewById(R.id.story_large_caption)
val largeCaptionOverlay: View = view.findViewById(R.id.story_large_caption_overlay)
val reactionAnimationView: OnReactionSentView = view.findViewById(R.id.on_reaction_sent_view)
@ -817,7 +818,7 @@ class StoryViewerPageFragment :
}
@SuppressLint("SetTextI18n")
private fun presentCaption(caption: TextView, largeCaption: TextView, largeCaptionOverlay: View, storyPost: StoryPost) {
private fun presentCaption(caption: EmojiTextView, largeCaption: TextView, largeCaptionOverlay: View, storyPost: StoryPost) {
val displayBody: CharSequence = if (storyPost.content is StoryPost.Content.AttachmentContent) {
val displayBodySpan = SpannableString(storyPost.content.attachment.caption ?: "")
val ranges: BodyRangeList? = storyPost.conversationMessage.messageRecord.messageRanges
@ -830,48 +831,24 @@ class StoryViewerPageFragment :
""
}
storyNormalBottomGradient.visible = !displayBody.isNotEmpty()
storyNormalBottomGradient.visible = displayBody.isEmpty()
storyCaptionBottomGradient.visible = displayBody.isNotEmpty()
caption.text = displayBody
largeCaption.text = displayBody
caption.visible = displayBody.isNotEmpty()
caption.requestLayout()
caption.movementMethod = LinkMovementMethod.getInstance()
caption.setOverflowText(getString(R.string.StoryViewerPageFragment__see_more))
caption.maxLines = 5
caption.text = displayBody
caption.doOnNextLayout {
val maxLines = 5
if (displayBody.isNotEmpty() && caption.lineCount > maxLines) {
val lastCharShown = caption.layout.getLineVisibleEnd(maxLines - 1)
caption.maxLines = maxLines
val seeMore = (getString(R.string.StoryViewerPageFragment__see_more))
val seeMoreWidth = caption.paint.measureText(seeMore)
var offset = seeMore.length
while (true) {
val start = lastCharShown - offset
if (start < 0) {
break
}
val widthOfRemovedChunk = caption.paint.measureText(displayBody.subSequence(start, lastCharShown).toString())
if (widthOfRemovedChunk > seeMoreWidth) {
break
}
offset += 1
}
caption.text = displayBody.substring(0, lastCharShown - offset) + seeMore
}
if (caption.text.length == displayBody.length) {
caption.setOnClickListener(null)
caption.isClickable = false
} else {
caption.setOnClickListener {
onShowCaptionOverlay(caption, largeCaption, largeCaptionOverlay)
}
if (caption.text.length == displayBody.length) {
caption.setOnClickListener(null)
caption.isClickable = false
} else {
caption.setOnClickListener {
onShowCaptionOverlay(caption, largeCaption, largeCaptionOverlay)
}
}
}

View file

@ -5218,7 +5218,7 @@
<!-- Displayed when copying group story reply text to clipboard -->
<string name="StoryGroupReplyFragment__copied_to_clipboard">Copied to clipboard</string>
<!-- Displayed in story caption when content is longer than 5 lines -->
<string name="StoryViewerPageFragment__see_more">See More</string>
<string name="StoryViewerPageFragment__see_more">See More</string>
<!-- Displayed in toast after sending a direct reply -->
<string name="StoryDirectReplyDialogFragment__sending_reply">Sending reply…</string>
<!-- Displayed in the viewer when a story is no longer available -->