That fuck shit the fascists are using
at master 161 lines 6.2 kB view raw
1package org.tm.archive.database; 2 3import android.content.Context; 4import android.text.SpannableStringBuilder; 5 6import androidx.annotation.NonNull; 7import androidx.annotation.Nullable; 8import androidx.annotation.VisibleForTesting; 9import androidx.annotation.WorkerThread; 10 11import com.annimon.stream.Stream; 12import com.annimon.stream.function.Function; 13 14import org.tm.archive.database.model.Mention; 15import org.tm.archive.database.model.MessageRecord; 16import org.tm.archive.database.model.databaseprotos.BodyRangeList; 17import org.tm.archive.recipients.Recipient; 18import org.tm.archive.recipients.RecipientId; 19import org.whispersystems.signalservice.api.push.ServiceId; 20 21import java.util.ArrayList; 22import java.util.Collections; 23import java.util.List; 24import java.util.SortedSet; 25import java.util.TreeSet; 26import java.util.stream.Collectors; 27 28public final class MentionUtil { 29 30 public static final char MENTION_STARTER = '@'; 31 static final String MENTION_PLACEHOLDER = "\uFFFC"; 32 33 private MentionUtil() { } 34 35 @WorkerThread 36 public static @NonNull UpdatedBodyAndMentions updateBodyWithDisplayNames(@NonNull Context context, @NonNull MessageRecord messageRecord) { 37 return updateBodyWithDisplayNames(context, messageRecord, messageRecord.getDisplayBody(context)); 38 } 39 40 @WorkerThread 41 public static @NonNull UpdatedBodyAndMentions updateBodyWithDisplayNames(@NonNull Context context, @NonNull MessageRecord messageRecord, @NonNull CharSequence body) { 42 List<Mention> mentions = SignalDatabase.mentions().getMentionsForMessage(messageRecord.getId()); 43 return updateBodyAndMentionsWithDisplayNames(context, body, mentions); 44 } 45 46 @WorkerThread 47 public static @NonNull UpdatedBodyAndMentions updateBodyAndMentionsWithDisplayNames(@NonNull Context context, @NonNull CharSequence body, @NonNull List<Mention> mentions) { 48 return update(body, mentions, m -> MENTION_STARTER + Recipient.resolved(m.getRecipientId()).getMentionDisplayName(context)); 49 } 50 51 public static @NonNull UpdatedBodyAndMentions updateBodyAndMentionsWithPlaceholders(@Nullable CharSequence body, @NonNull List<Mention> mentions) { 52 return update(body, mentions, m -> MENTION_PLACEHOLDER); 53 } 54 55 @VisibleForTesting 56 static @NonNull UpdatedBodyAndMentions update(@Nullable CharSequence body, @NonNull List<Mention> mentions, @NonNull Function<Mention, CharSequence> replacementTextGenerator) { 57 if (body == null || mentions.isEmpty()) { 58 return new UpdatedBodyAndMentions(body, mentions, Collections.emptyList()); 59 } 60 61 SortedSet<Mention> sortedMentions = new TreeSet<>(mentions); 62 SpannableStringBuilder updatedBody = new SpannableStringBuilder(); 63 List<Mention> updatedMentions = new ArrayList<>(); 64 List<BodyAdjustment> bodyAdjustments = new ArrayList<>(); 65 66 int bodyIndex = 0; 67 68 for (Mention mention : sortedMentions) { 69 if (invalidMention(body, mention) || bodyIndex > mention.getStart()) { 70 continue; 71 } 72 73 updatedBody.append(body.subSequence(bodyIndex, mention.getStart())); 74 CharSequence replaceWith = replacementTextGenerator.apply(mention); 75 Mention updatedMention = new Mention(mention.getRecipientId(), updatedBody.length(), replaceWith.length()); 76 77 updatedBody.append(replaceWith); 78 updatedMentions.add(updatedMention); 79 80 bodyAdjustments.add(new BodyAdjustment(mention.getStart(), mention.getLength(), updatedMention.getLength())); 81 82 bodyIndex = mention.getStart() + mention.getLength(); 83 } 84 85 if (bodyIndex < body.length()) { 86 updatedBody.append(body.subSequence(bodyIndex, body.length())); 87 } 88 89 return new UpdatedBodyAndMentions(updatedBody, updatedMentions, bodyAdjustments); 90 } 91 92 public static @Nullable BodyRangeList mentionsToBodyRangeList(@Nullable List<Mention> mentions) { 93 if (mentions == null || mentions.isEmpty()) { 94 return null; 95 } 96 97 BodyRangeList.Builder builder = new BodyRangeList.Builder(); 98 builder.ranges( 99 mentions.stream() 100 .map(mention -> { 101 String uuid = Recipient.resolved(mention.getRecipientId()).requireAci().toString(); 102 return new BodyRangeList.BodyRange.Builder() 103 .mentionUuid(uuid) 104 .start(mention.getStart()) 105 .length(mention.getLength()) 106 .build(); 107 }) 108 .collect(Collectors.toList()) 109 ); 110 return builder.build(); 111 } 112 113 public static @NonNull List<Mention> bodyRangeListToMentions(@Nullable BodyRangeList bodyRanges) { 114 if (bodyRanges != null) { 115 return Stream.of(bodyRanges.ranges) 116 .filter(bodyRange -> bodyRange.mentionUuid != null) 117 .map(mention -> { 118 RecipientId id = Recipient.externalPush(ServiceId.parseOrThrow(mention.mentionUuid)).getId(); 119 return new Mention(id, mention.start, mention.length); 120 }) 121 .toList(); 122 } else { 123 return Collections.emptyList(); 124 } 125 } 126 127 private static boolean invalidMention(@NonNull CharSequence body, @NonNull Mention mention) { 128 int start = mention.getStart(); 129 int length = mention.getLength(); 130 131 return start < 0 || length < 0 || (start + length) > body.length(); 132 } 133 134 public static class UpdatedBodyAndMentions { 135 @Nullable private final CharSequence body; 136 @NonNull private final List<Mention> mentions; 137 @NonNull private final List<BodyAdjustment> bodyAdjustments; 138 139 private UpdatedBodyAndMentions(@Nullable CharSequence body, @NonNull List<Mention> mentions, @NonNull List<BodyAdjustment> bodyAdjustments) { 140 this.body = body; 141 this.mentions = mentions; 142 this.bodyAdjustments = bodyAdjustments; 143 } 144 145 public @Nullable CharSequence getBody() { 146 return body; 147 } 148 149 public @NonNull List<Mention> getMentions() { 150 return mentions; 151 } 152 153 public @NonNull List<BodyAdjustment> getBodyAdjustments() { 154 return bodyAdjustments; 155 } 156 157 @Nullable String getBodyAsString() { 158 return body != null ? body.toString() : null; 159 } 160 } 161}