Fix multiple issues with rendering spoilers as story captions.
This commit is contained in:
parent
a837f86999
commit
b9d7d19dea
4 changed files with 47 additions and 59 deletions
|
@ -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) &&
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 -->
|
||||
|
|
Loading…
Add table
Reference in a new issue