Add spoiler format style.

This commit is contained in:
Cody Henthorne
2023-03-22 16:42:30 -04:00
parent 5d6889786c
commit 43cd647036
20 changed files with 662 additions and 57 deletions

View File

@@ -143,6 +143,7 @@ import org.thoughtcrime.securesms.components.reminder.ReminderView;
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder;
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder;
import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsActivity;
import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation;
import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
@@ -493,6 +494,7 @@ public class ConversationParentFragment extends Fragment
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
disposables.bindTo(getViewLifecycleOwner());
menuProvider = new ConversationOptionsMenu.Provider(this, this, disposables);
SpoilerAnnotation.resetRevealedSpoilers();
if (requireActivity() instanceof Callback) {
callback = (Callback) requireActivity();

View File

@@ -1,12 +1,14 @@
package org.thoughtcrime.securesms.conversation
import android.graphics.Typeface
import android.text.Annotation
import android.text.Spannable
import android.text.Spanned
import android.text.style.CharacterStyle
import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan
import android.text.style.TypefaceSpan
import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.util.PlaceholderURLSpan
@@ -39,7 +41,13 @@ object MessageStyler {
}
@JvmStatic
fun style(messageRanges: BodyRangeList?, span: Spannable): Result {
fun spoilerStyle(start: Int, length: Int, body: Spannable? = null): Annotation {
return SpoilerAnnotation.spoilerAnnotation(arrayOf(start, length, body?.toString()).contentHashCode())
}
@JvmStatic
@JvmOverloads
fun style(messageRanges: BodyRangeList?, span: Spannable, hideSpoilerText: Boolean = true): Result {
if (messageRanges == null) {
return Result.none()
}
@@ -53,11 +61,18 @@ object MessageStyler {
.filter { r -> r.start >= 0 && r.start < span.length && r.start + r.length >= 0 && r.start + r.length <= span.length }
.forEach { range ->
if (range.hasStyle()) {
val styleSpan: CharacterStyle? = when (range.style) {
val styleSpan: Any? = when (range.style) {
BodyRangeList.BodyRange.Style.BOLD -> boldStyle()
BodyRangeList.BodyRange.Style.ITALIC -> italicStyle()
BodyRangeList.BodyRange.Style.STRIKETHROUGH -> strikethroughStyle()
BodyRangeList.BodyRange.Style.MONOSPACE -> monoStyle()
BodyRangeList.BodyRange.Style.SPOILER -> {
val spoiler = spoilerStyle(range.start, range.length, span)
if (hideSpoilerText) {
span.setSpan(SpoilerAnnotation.SpoilerClickableSpan(spoiler), range.start, range.start + range.length, SPAN_FLAGS)
}
spoiler
}
else -> null
}
@@ -83,34 +98,46 @@ object MessageStyler {
@JvmStatic
fun hasStyling(text: Spanned): Boolean {
return text
.getSpans(0, text.length, CharacterStyle::class.java)
.any { s -> isSupportedCharacterStyle(s) && text.getSpanEnd(s) - text.getSpanStart(s) > 0 }
.getSpans(0, text.length, Object::class.java)
.any { s -> s.isSupportedStyle() && text.getSpanEnd(s) - text.getSpanStart(s) > 0 }
}
@JvmStatic
fun getStyling(text: CharSequence?): BodyRangeList? {
val bodyRanges = if (text is Spanned) {
text
.getSpans(0, text.length, CharacterStyle::class.java)
.filter { s -> isSupportedCharacterStyle(s) }
.mapNotNull { span: CharacterStyle ->
.getSpans(0, text.length, Object::class.java)
.filter { s -> s.isSupportedStyle() }
.mapNotNull { span ->
val spanStart = text.getSpanStart(span)
val spanLength = text.getSpanEnd(span) - spanStart
val style = when (span) {
is StyleSpan -> if (span.style == Typeface.BOLD) BodyRangeList.BodyRange.Style.BOLD else BodyRangeList.BodyRange.Style.ITALIC
val style: BodyRangeList.BodyRange.Style? = when (span) {
is StyleSpan -> {
when (span.style) {
Typeface.BOLD -> BodyRangeList.BodyRange.Style.BOLD
Typeface.ITALIC -> BodyRangeList.BodyRange.Style.ITALIC
else -> null
}
}
is StrikethroughSpan -> BodyRangeList.BodyRange.Style.STRIKETHROUGH
is TypefaceSpan -> BodyRangeList.BodyRange.Style.MONOSPACE
is Annotation -> {
if (SpoilerAnnotation.isSpoilerAnnotation(span)) {
BodyRangeList.BodyRange.Style.SPOILER
} else {
null
}
}
else -> throw IllegalArgumentException("Provided text contains unsupported spans")
}
if (spanLength > 0) {
if (spanLength > 0 && style != null) {
BodyRangeList.BodyRange.newBuilder().setStart(spanStart).setLength(spanLength).setStyle(style).build()
} else {
null
}
}
.toList()
} else {
emptyList()
}
@@ -122,8 +149,15 @@ object MessageStyler {
}
}
@JvmStatic
fun isSupportedCharacterStyle(style: CharacterStyle): Boolean {
fun Any.isSupportedStyle(): Boolean {
return when (this) {
is CharacterStyle -> isSupportedCharacterStyle(this)
is Annotation -> SpoilerAnnotation.isSpoilerAnnotation(this)
else -> false
}
}
private fun isSupportedCharacterStyle(style: CharacterStyle): Boolean {
return when (style) {
is StyleSpan -> style.style == Typeface.ITALIC || style.style == Typeface.BOLD
is StrikethroughSpan -> true

View File

@@ -79,7 +79,7 @@ class DraftRepository(
updatedText = SpannableString(updated.body)
MentionAnnotation.setMentionAnnotations(updatedText, updated.mentions)
MessageStyler.style(bodyRanges.adjustBodyRanges(updated.bodyAdjustments), updatedText)
MessageStyler.style(messageRanges = bodyRanges.adjustBodyRanges(updated.bodyAdjustments), span = updatedText, hideSpoilerText = false)
}
DatabaseDraft(drafts, updatedText)