164 lines
6.5 KiB
Java
164 lines
6.5 KiB
Java
package org.thoughtcrime.securesms.database;
|
|
|
|
import android.content.Context;
|
|
import android.text.SpannableStringBuilder;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.WorkerThread;
|
|
|
|
import com.annimon.stream.Stream;
|
|
import com.annimon.stream.function.Function;
|
|
import com.google.protobuf.InvalidProtocolBufferException;
|
|
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.database.RecipientDatabase.MentionSetting;
|
|
import org.thoughtcrime.securesms.database.model.Mention;
|
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.SortedSet;
|
|
import java.util.TreeSet;
|
|
|
|
public final class MentionUtil {
|
|
|
|
public static final char MENTION_STARTER = '@';
|
|
static final String MENTION_PLACEHOLDER = "\uFFFC";
|
|
|
|
private MentionUtil() { }
|
|
|
|
@WorkerThread
|
|
public static @NonNull CharSequence updateBodyWithDisplayNames(@NonNull Context context, @NonNull MessageRecord messageRecord) {
|
|
return updateBodyWithDisplayNames(context, messageRecord, messageRecord.getDisplayBody(context));
|
|
}
|
|
|
|
@WorkerThread
|
|
public static @NonNull CharSequence updateBodyWithDisplayNames(@NonNull Context context, @NonNull MessageRecord messageRecord, @NonNull CharSequence body) {
|
|
if (messageRecord.isMms()) {
|
|
List<Mention> mentions = DatabaseFactory.getMentionDatabase(context).getMentionsForMessage(messageRecord.getId());
|
|
CharSequence updated = updateBodyAndMentionsWithDisplayNames(context, body, mentions).getBody();
|
|
if (updated != null) {
|
|
return updated;
|
|
}
|
|
}
|
|
return body;
|
|
}
|
|
|
|
@WorkerThread
|
|
public static @NonNull UpdatedBodyAndMentions updateBodyAndMentionsWithDisplayNames(@NonNull Context context, @NonNull MessageRecord messageRecord, @NonNull List<Mention> mentions) {
|
|
return updateBodyAndMentionsWithDisplayNames(context, messageRecord.getDisplayBody(context), mentions);
|
|
}
|
|
|
|
@WorkerThread
|
|
public static @NonNull UpdatedBodyAndMentions updateBodyAndMentionsWithDisplayNames(@NonNull Context context, @NonNull CharSequence body, @NonNull List<Mention> mentions) {
|
|
return update(body, mentions, m -> MENTION_STARTER + Recipient.resolved(m.getRecipientId()).getMentionDisplayName(context));
|
|
}
|
|
|
|
public static @NonNull UpdatedBodyAndMentions updateBodyAndMentionsWithPlaceholders(@Nullable CharSequence body, @NonNull List<Mention> mentions) {
|
|
return update(body, mentions, m -> MENTION_PLACEHOLDER);
|
|
}
|
|
|
|
private static @NonNull UpdatedBodyAndMentions update(@Nullable CharSequence body, @NonNull List<Mention> mentions, @NonNull Function<Mention, CharSequence> replacementTextGenerator) {
|
|
if (body == null || mentions.isEmpty()) {
|
|
return new UpdatedBodyAndMentions(body, mentions);
|
|
}
|
|
|
|
SortedSet<Mention> sortedMentions = new TreeSet<>(mentions);
|
|
SpannableStringBuilder updatedBody = new SpannableStringBuilder();
|
|
List<Mention> updatedMentions = new ArrayList<>();
|
|
|
|
int bodyIndex = 0;
|
|
|
|
for (Mention mention : sortedMentions) {
|
|
updatedBody.append(body.subSequence(bodyIndex, mention.getStart()));
|
|
CharSequence replaceWith = replacementTextGenerator.apply(mention);
|
|
Mention updatedMention = new Mention(mention.getRecipientId(), updatedBody.length(), replaceWith.length());
|
|
|
|
updatedBody.append(replaceWith);
|
|
updatedMentions.add(updatedMention);
|
|
|
|
bodyIndex = mention.getStart() + mention.getLength();
|
|
}
|
|
|
|
if (bodyIndex < body.length()) {
|
|
updatedBody.append(body.subSequence(bodyIndex, body.length()));
|
|
}
|
|
|
|
return new UpdatedBodyAndMentions(updatedBody.toString(), updatedMentions);
|
|
}
|
|
|
|
public static @Nullable BodyRangeList mentionsToBodyRangeList(@Nullable List<Mention> mentions) {
|
|
if (mentions == null || mentions.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
BodyRangeList.Builder builder = BodyRangeList.newBuilder();
|
|
|
|
for (Mention mention : mentions) {
|
|
String uuid = Recipient.resolved(mention.getRecipientId()).requireUuid().toString();
|
|
builder.addRanges(BodyRangeList.BodyRange.newBuilder()
|
|
.setMentionUuid(uuid)
|
|
.setStart(mention.getStart())
|
|
.setLength(mention.getLength()));
|
|
}
|
|
|
|
return builder.build();
|
|
}
|
|
|
|
public static @NonNull List<Mention> bodyRangeListToMentions(@NonNull Context context, @Nullable byte[] data) {
|
|
if (data != null) {
|
|
try {
|
|
return Stream.of(BodyRangeList.parseFrom(data).getRangesList())
|
|
.filter(bodyRange -> bodyRange.getAssociatedValueCase() == BodyRangeList.BodyRange.AssociatedValueCase.MENTIONUUID)
|
|
.map(mention -> {
|
|
RecipientId id = Recipient.externalPush(context, UuidUtil.parseOrThrow(mention.getMentionUuid()), null, false).getId();
|
|
return new Mention(id, mention.getStart(), mention.getLength());
|
|
})
|
|
.toList();
|
|
} catch (InvalidProtocolBufferException e) {
|
|
return Collections.emptyList();
|
|
}
|
|
} else {
|
|
return Collections.emptyList();
|
|
}
|
|
}
|
|
|
|
public static @NonNull String getMentionSettingDisplayValue(@NonNull Context context, @NonNull MentionSetting mentionSetting) {
|
|
switch (mentionSetting) {
|
|
case ALWAYS_NOTIFY:
|
|
return context.getString(R.string.GroupMentionSettingDialog_always_notify_me);
|
|
case DO_NOT_NOTIFY:
|
|
return context.getString(R.string.GroupMentionSettingDialog_dont_notify_me);
|
|
}
|
|
throw new IllegalArgumentException("Unknown mention setting: " + mentionSetting);
|
|
}
|
|
|
|
public static class UpdatedBodyAndMentions {
|
|
@Nullable private final CharSequence body;
|
|
@NonNull private final List<Mention> mentions;
|
|
|
|
public UpdatedBodyAndMentions(@Nullable CharSequence body, @NonNull List<Mention> mentions) {
|
|
this.body = body;
|
|
this.mentions = mentions;
|
|
}
|
|
|
|
public @Nullable CharSequence getBody() {
|
|
return body;
|
|
}
|
|
|
|
public @NonNull List<Mention> getMentions() {
|
|
return mentions;
|
|
}
|
|
|
|
@Nullable String getBodyAsString() {
|
|
return body != null ? body.toString() : null;
|
|
}
|
|
}
|
|
}
|