That fuck shit the fascists are using
1package org.tm.archive.database;
2
3import android.content.Context;
4import android.text.SpannableStringBuilder;
5import android.text.TextUtils;
6
7import androidx.annotation.NonNull;
8import androidx.annotation.Nullable;
9import androidx.annotation.StringRes;
10
11import org.signal.core.util.logging.Log;
12import org.tm.archive.R;
13import org.tm.archive.components.emoji.EmojiStrings;
14import org.tm.archive.contactshare.Contact;
15import org.tm.archive.contactshare.ContactUtil;
16import org.tm.archive.database.model.MessageRecord;
17import org.tm.archive.database.model.MmsMessageRecord;
18import org.tm.archive.mms.GifSlide;
19import org.tm.archive.mms.Slide;
20import org.tm.archive.mms.StickerSlide;
21import org.tm.archive.util.MessageRecordUtil;
22import org.tm.archive.util.Util;
23
24import java.util.Collections;
25import java.util.List;
26import java.util.Objects;
27
28public final class ThreadBodyUtil {
29
30 private static final String TAG = Log.tag(ThreadBodyUtil.class);
31
32 private ThreadBodyUtil() {
33 }
34
35 public static @NonNull ThreadBody getFormattedBodyFor(@NonNull Context context, @NonNull MessageRecord record) {
36 if (record.isMms()) {
37 return getFormattedBodyForMms(context, (MmsMessageRecord) record, null);
38 }
39
40 return new ThreadBody(record.getBody());
41 }
42
43 public static @NonNull CharSequence getFormattedBodyForNotification(@NonNull Context context, @NonNull MessageRecord record, @Nullable CharSequence bodyOverride) {
44 return getFormattedBodyForMms(context, (MmsMessageRecord) record, bodyOverride).body;
45 }
46
47 private static @NonNull ThreadBody getFormattedBodyForMms(@NonNull Context context, @NonNull MmsMessageRecord record, @Nullable CharSequence bodyOverride) {
48 if (record.getSharedContacts().size() > 0) {
49 Contact contact = record.getSharedContacts().get(0);
50
51 return new ThreadBody(ContactUtil.getStringSummary(context, contact).toString());
52 } else if (record.getSlideDeck().getDocumentSlide() != null) {
53 return format(context, record, EmojiStrings.FILE, R.string.ThreadRecord_file, bodyOverride);
54 } else if (record.getSlideDeck().getAudioSlide() != null) {
55 return format(context, record, EmojiStrings.AUDIO, R.string.ThreadRecord_voice_message, bodyOverride);
56 } else if (MessageRecordUtil.hasSticker(record)) {
57 String emoji = getStickerEmoji(record);
58 return format(context, record, emoji, R.string.ThreadRecord_sticker, bodyOverride);
59 } else if (MessageRecordUtil.hasGiftBadge(record)) {
60 return format(EmojiStrings.GIFT, getGiftSummary(context, record));
61 } else if (MessageRecordUtil.isStoryReaction(record)) {
62 return new ThreadBody(getStoryReactionSummary(context, record));
63 } else if (record.isPaymentNotification()) {
64 return format(EmojiStrings.CARD, context.getString(R.string.ThreadRecord_payment));
65 } else if (record.isPaymentsRequestToActivate()) {
66 return format(EmojiStrings.CARD, getPaymentActivationRequestSummary(context, record));
67 } else if (record.isPaymentsActivated()) {
68 return format(EmojiStrings.CARD, getPaymentActivatedSummary(context, record));
69 } else if (record.isCallLog() && !record.isGroupCall()) {
70 return new ThreadBody(getCallLogSummary(context, record));
71 } else if (MessageRecordUtil.isScheduled(record)) {
72 return new ThreadBody(context.getString(R.string.ThreadRecord_scheduled_message));
73 }
74
75 boolean hasImage = false;
76 boolean hasVideo = false;
77 boolean hasGif = false;
78
79 for (Slide slide : record.getSlideDeck().getSlides()) {
80 hasVideo |= slide.hasVideo();
81 hasImage |= slide.hasImage();
82 hasGif |= slide instanceof GifSlide || slide.isVideoGif();
83 }
84
85 if (hasGif) {
86 return format(context, record, EmojiStrings.GIF, R.string.ThreadRecord_gif, bodyOverride);
87 } else if (hasVideo) {
88 return format(context, record, EmojiStrings.VIDEO, R.string.ThreadRecord_video, bodyOverride);
89 } else if (hasImage) {
90 return format(context, record, EmojiStrings.PHOTO, R.string.ThreadRecord_photo, bodyOverride);
91 } else if (TextUtils.isEmpty(record.getBody())) {
92 return new ThreadBody(context.getString(R.string.ThreadRecord_media_message));
93 } else {
94 return getBody(context, record);
95 }
96 }
97
98 private static @NonNull String getGiftSummary(@NonNull Context context, @NonNull MessageRecord messageRecord) {
99 if (messageRecord.isOutgoing()) {
100 return context.getString(R.string.ThreadRecord__you_donated_for_s, messageRecord.getToRecipient().getShortDisplayName(context));
101 } else if (messageRecord.isViewed()) {
102 return context.getString(R.string.ThreadRecord__you_redeemed_a_badge);
103 } else {
104 return context.getString(R.string.ThreadRecord__s_donated_for_you, messageRecord.getFromRecipient().getShortDisplayName(context));
105 }
106 }
107
108 private static @NonNull String getStoryReactionSummary(@NonNull Context context, @NonNull MessageRecord messageRecord) {
109 if (messageRecord.isOutgoing()) {
110 return context.getString(R.string.ThreadRecord__reacted_s_to_their_story, messageRecord.getDisplayBody(context));
111 } else {
112 return context.getString(R.string.ThreadRecord__reacted_s_to_your_story, messageRecord.getDisplayBody(context));
113 }
114 }
115
116 private static @NonNull String getPaymentActivationRequestSummary(@NonNull Context context, @NonNull MessageRecord messageRecord) {
117 if (messageRecord.isOutgoing()) {
118 return context.getString(R.string.ThreadRecord_you_sent_request);
119 } else {
120 return context.getString(R.string.ThreadRecord_wants_you_to_activate_payments, messageRecord.getFromRecipient().getShortDisplayName(context));
121 }
122 }
123
124 private static @NonNull String getPaymentActivatedSummary(@NonNull Context context, @NonNull MessageRecord messageRecord) {
125 if (messageRecord.isOutgoing()) {
126 return context.getString(R.string.ThreadRecord_you_activated_payments);
127 } else {
128 return context.getString(R.string.ThreadRecord_can_accept_payments, messageRecord.getFromRecipient().getShortDisplayName(context));
129 }
130 }
131
132 private static @NonNull String getCallLogSummary(@NonNull Context context, @NonNull MessageRecord record) {
133 CallTable.Call call = SignalDatabase.calls().getCallByMessageId(record.getId());
134 if (call != null) {
135 boolean accepted = call.getEvent() == CallTable.Event.ACCEPTED;
136 if (call.getDirection() == CallTable.Direction.OUTGOING) {
137 if (call.getType() == CallTable.Type.AUDIO_CALL) {
138 return context.getString(accepted ? R.string.MessageRecord_outgoing_voice_call : R.string.MessageRecord_unanswered_voice_call);
139 } else {
140 return context.getString(accepted ? R.string.MessageRecord_outgoing_video_call : R.string.MessageRecord_unanswered_video_call);
141 }
142 } else {
143 boolean isVideoCall = call.getType() == CallTable.Type.VIDEO_CALL;
144 boolean isMissed = call.getEvent().isMissedCall();
145
146 if (accepted) {
147 return context.getString(isVideoCall ? R.string.MessageRecord_incoming_video_call : R.string.MessageRecord_incoming_voice_call);
148 } else if (isMissed) {
149 if (call.getEvent() == CallTable.Event.MISSED_NOTIFICATION_PROFILE) {
150 return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call_notification_profile) : context.getString(R.string.MessageRecord_missed_voice_call_notification_profile);
151 } else {
152 return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call) : context.getString(R.string.MessageRecord_missed_voice_call);
153 }
154 } else {
155 return isVideoCall ? context.getString(R.string.MessageRecord_you_declined_a_video_call) : context.getString(R.string.MessageRecord_you_declined_a_voice_call);
156 }
157 }
158 } else {
159 return "";
160 }
161 }
162
163 private static @NonNull ThreadBody format(@NonNull Context context,
164 @NonNull MessageRecord record,
165 @NonNull String emoji,
166 @StringRes int defaultStringRes,
167 @Nullable CharSequence bodyOverride)
168 {
169 CharSequence body = !TextUtils.isEmpty(bodyOverride) ? bodyOverride
170 : TextUtils.isEmpty(record.getBody()) ? context.getString(defaultStringRes)
171 : getBody(context, record).getBody();
172 return format(emoji, body);
173 }
174
175 private static @NonNull ThreadBody format(@NonNull CharSequence prefix, @NonNull CharSequence body) {
176 SpannableStringBuilder builder = new SpannableStringBuilder();
177 builder.append(prefix)
178 .append(" ")
179 .append(body);
180 return new ThreadBody(builder, prefix.length() + 1);
181 }
182
183 private static @NonNull ThreadBody getBody(@NonNull Context context, @NonNull MessageRecord record) {
184 MentionUtil.UpdatedBodyAndMentions updated = MentionUtil.updateBodyWithDisplayNames(context, record, record.getBody());
185 //noinspection ConstantConditions
186 return new ThreadBody(updated.getBody(), updated.getBodyAdjustments());
187 }
188
189 private static @NonNull String getStickerEmoji(@NonNull MessageRecord record) {
190 StickerSlide slide = Objects.requireNonNull(((MmsMessageRecord) record).getSlideDeck().getStickerSlide());
191
192 return Util.isEmpty(slide.getEmoji()) ? EmojiStrings.STICKER
193 : slide.getEmoji();
194 }
195
196 public static class ThreadBody {
197 private final CharSequence body;
198 private final List<BodyAdjustment> bodyAdjustments;
199
200 public ThreadBody(@NonNull CharSequence body) {
201 this(body, 0);
202 }
203
204 public ThreadBody(@NonNull CharSequence body, int startOffset) {
205 this(body, startOffset == 0 ? Collections.emptyList() : Collections.singletonList(new BodyAdjustment(0, 0, startOffset)));
206 }
207
208 public ThreadBody(@NonNull CharSequence body, @NonNull List<BodyAdjustment> bodyAdjustments) {
209 this.body = body;
210 this.bodyAdjustments = bodyAdjustments;
211 }
212
213 public @NonNull CharSequence getBody() {
214 return body;
215 }
216
217 public @NonNull List<BodyAdjustment> getBodyAdjustments() {
218 return bodyAdjustments;
219 }
220 }
221}