That fuck shit the fascists are using
1package org.tm.archive.sharing;
2
3import android.net.Uri;
4import android.os.Parcel;
5import android.os.Parcelable;
6import android.text.TextUtils;
7
8import androidx.annotation.NonNull;
9import androidx.annotation.Nullable;
10
11import com.annimon.stream.Stream;
12
13import org.signal.core.util.BreakIteratorCompat;
14import org.signal.core.util.logging.Log;
15import org.tm.archive.contacts.paged.ContactSearchKey;
16import org.tm.archive.contactshare.Contact;
17import org.tm.archive.database.model.Mention;
18import org.tm.archive.database.model.databaseprotos.BodyRangeList;
19import org.tm.archive.linkpreview.LinkPreview;
20import org.tm.archive.mediasend.Media;
21import org.tm.archive.stickers.StickerLocator;
22import org.tm.archive.stories.Stories;
23import org.tm.archive.util.MediaUtil;
24import org.tm.archive.util.ParcelUtil;
25import org.tm.archive.util.Util;
26
27import java.io.IOException;
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.HashSet;
31import java.util.List;
32import java.util.Set;
33import java.util.stream.Collectors;
34
35public final class MultiShareArgs implements Parcelable {
36
37 private static final String TAG = Log.tag(MultiShareArgs.class);
38
39 private final Set<ContactSearchKey> contactSearchKeys;
40 private final List<Media> media;
41 private final String draftText;
42 private final StickerLocator stickerLocator;
43 private final boolean borderless;
44 private final Uri dataUri;
45 private final String dataType;
46 private final boolean viewOnce;
47 private final LinkPreview linkPreview;
48 private final List<Mention> mentions;
49 private final long timestamp;
50 private final long expiresAt;
51 private final boolean isTextStory;
52 private final BodyRangeList bodyRanges;
53 private final List<Contact> sharedContacts;
54
55 private MultiShareArgs(@NonNull Builder builder) {
56 contactSearchKeys = builder.contactSearchKeys;
57 media = builder.media == null ? new ArrayList<>() : new ArrayList<>(builder.media);
58 draftText = builder.draftText;
59 stickerLocator = builder.stickerLocator;
60 borderless = builder.borderless;
61 dataUri = builder.dataUri;
62 dataType = builder.dataType;
63 viewOnce = builder.viewOnce;
64 linkPreview = builder.linkPreview;
65 mentions = builder.mentions == null ? new ArrayList<>() : new ArrayList<>(builder.mentions);
66 timestamp = builder.timestamp;
67 expiresAt = builder.expiresAt;
68 isTextStory = builder.isTextStory;
69 bodyRanges = builder.bodyRanges;
70 sharedContacts = builder.sharedContacts == null ? new ArrayList<>() : new ArrayList<>(builder.sharedContacts);
71 }
72
73 protected MultiShareArgs(Parcel in) {
74 List<ContactSearchKey.RecipientSearchKey> parcelableRecipientSearchKeys = in.createTypedArrayList(ContactSearchKey.RecipientSearchKey.CREATOR);
75
76 contactSearchKeys = new HashSet<>(parcelableRecipientSearchKeys);
77 media = in.createTypedArrayList(Media.CREATOR);
78 draftText = in.readString();
79 stickerLocator = in.readParcelable(StickerLocator.class.getClassLoader());
80 borderless = in.readByte() != 0;
81 dataUri = in.readParcelable(Uri.class.getClassLoader());
82 dataType = in.readString();
83 viewOnce = in.readByte() != 0;
84 mentions = in.createTypedArrayList(Mention.CREATOR);
85 timestamp = in.readLong();
86 expiresAt = in.readLong();
87 isTextStory = ParcelUtil.readBoolean(in);
88
89 String linkedPreviewString = in.readString();
90 LinkPreview preview;
91 try {
92 preview = linkedPreviewString != null ? LinkPreview.deserialize(linkedPreviewString) : null;
93 } catch (IOException e) {
94 preview = null;
95 }
96
97 linkPreview = preview;
98
99 BodyRangeList bodyRanges = null;
100 try {
101 byte[] data = ParcelUtil.readByteArray(in);
102 if (data != null) {
103 bodyRanges = BodyRangeList.ADAPTER.decode(data);
104 }
105 } catch (IOException e) {
106 Log.w(TAG, "Invalid body range", e);
107 }
108 this.bodyRanges = bodyRanges;
109 sharedContacts = in.createTypedArrayList(Contact.CREATOR);
110 }
111
112 public Set<ContactSearchKey> getContactSearchKeys() {
113 return contactSearchKeys;
114 }
115
116 public Set<ContactSearchKey.RecipientSearchKey> getRecipientSearchKeys() {
117 return contactSearchKeys.stream()
118 .filter(key -> key instanceof ContactSearchKey.RecipientSearchKey)
119 .map(key -> (ContactSearchKey.RecipientSearchKey) key)
120 .collect(Collectors.toSet());
121 }
122
123 public @NonNull List<Media> getMedia() {
124 return media;
125 }
126
127 public StickerLocator getStickerLocator() {
128 return stickerLocator;
129 }
130
131 public String getDataType() {
132 return dataType;
133 }
134
135 public @Nullable String getDraftText() {
136 return draftText;
137 }
138
139 public Uri getDataUri() {
140 return dataUri;
141 }
142
143 public boolean isBorderless() {
144 return borderless;
145 }
146
147 public boolean isViewOnce() {
148 return viewOnce;
149 }
150
151 public boolean isTextStory() {
152 return isTextStory;
153 }
154
155 public @Nullable LinkPreview getLinkPreview() {
156 return linkPreview;
157 }
158
159 public @NonNull List<Mention> getMentions() {
160 return mentions;
161 }
162
163 public long getTimestamp() {
164 return timestamp;
165 }
166
167 public long getExpiresAt() {
168 return expiresAt;
169 }
170
171 public @Nullable BodyRangeList getBodyRanges() {
172 return bodyRanges;
173 }
174
175 public @NonNull List<Contact> getSharedContacts() {
176 return sharedContacts;
177 }
178
179 public boolean isValidForStories() {
180 if (isViewOnce() || stickerLocator != null) {
181 return false;
182 }
183
184 return isTextStory ||
185 (!media.isEmpty() && media.stream().allMatch(m -> MediaUtil.isStorySupportedType(m.getMimeType()))) ||
186 MediaUtil.isStorySupportedType(dataType) ||
187 isValidForTextStoryGeneration();
188 }
189
190 public boolean isValidForNonStories() {
191 return !isTextStory;
192 }
193
194 public boolean isValidForTextStoryGeneration() {
195 if (isTextStory || !media.isEmpty()) {
196 return false;
197 }
198
199 if (!Util.isEmpty(getDraftText())) {
200 BreakIteratorCompat breakIteratorCompat = BreakIteratorCompat.getInstance();
201 breakIteratorCompat.setText(getDraftText());
202
203 if (breakIteratorCompat.countBreaks() > Stories.MAX_TEXT_STORY_SIZE) {
204 return false;
205 }
206 }
207
208 return linkPreview != null || !Util.isEmpty(draftText);
209 }
210
211 public @NonNull InterstitialContentType getInterstitialContentType() {
212 if (!requiresInterstitial()) {
213 return InterstitialContentType.NONE;
214 } else if (!this.getMedia().isEmpty() ||
215 (this.getDataUri() != null && this.getDataUri() != Uri.EMPTY && this.getDataType() != null && MediaUtil.isImageOrVideoType(this.getDataType())))
216 {
217 return InterstitialContentType.MEDIA;
218 } else if (!TextUtils.isEmpty(this.getDraftText()) && allRecipientsAreStories()) {
219 return InterstitialContentType.MEDIA;
220 } else if (!TextUtils.isEmpty(this.getDraftText())) {
221 return InterstitialContentType.TEXT;
222 } else {
223 return InterstitialContentType.NONE;
224 }
225 }
226
227 public boolean allRecipientsAreStories() {
228 return !contactSearchKeys.isEmpty() && contactSearchKeys.stream()
229 .allMatch(key -> key.requireRecipientSearchKey().isStory());
230 }
231
232 public static final Creator<MultiShareArgs> CREATOR = new Creator<MultiShareArgs>() {
233 @Override
234 public MultiShareArgs createFromParcel(Parcel in) {
235 return new MultiShareArgs(in);
236 }
237
238 @Override
239 public MultiShareArgs[] newArray(int size) {
240 return new MultiShareArgs[size];
241 }
242 };
243
244 @Override
245 public int describeContents() {
246 return 0;
247 }
248
249 @Override
250 public void writeToParcel(Parcel dest, int flags) {
251 dest.writeTypedList(Stream.of(contactSearchKeys).map(ContactSearchKey::requireRecipientSearchKey).toList());
252 dest.writeTypedList(media);
253 dest.writeString(draftText);
254 dest.writeParcelable(stickerLocator, flags);
255 dest.writeByte((byte) (borderless ? 1 : 0));
256 dest.writeParcelable(dataUri, flags);
257 dest.writeString(dataType);
258 dest.writeByte((byte) (viewOnce ? 1 : 0));
259 dest.writeTypedList(mentions);
260 dest.writeLong(timestamp);
261 dest.writeLong(expiresAt);
262 ParcelUtil.writeBoolean(dest, isTextStory);
263
264 if (linkPreview != null) {
265 try {
266 dest.writeString(linkPreview.serialize());
267 } catch (IOException e) {
268 dest.writeString("");
269 }
270 } else {
271 dest.writeString("");
272 }
273
274 if (bodyRanges != null) {
275 ParcelUtil.writeByteArray(dest, bodyRanges.encode());
276 } else {
277 ParcelUtil.writeByteArray(dest, null);
278 }
279
280 dest.writeTypedList(sharedContacts);
281 }
282
283 public Builder buildUpon() {
284 return buildUpon(contactSearchKeys);
285 }
286
287 public Builder buildUpon(@NonNull Set<ContactSearchKey> recipientSearchKeys) {
288 return new Builder(recipientSearchKeys).asBorderless(borderless)
289 .asViewOnce(viewOnce)
290 .withDataType(dataType)
291 .withDataUri(dataUri)
292 .withDraftText(draftText)
293 .withLinkPreview(linkPreview)
294 .withMedia(media)
295 .withStickerLocator(stickerLocator)
296 .withMentions(mentions)
297 .withTimestamp(timestamp)
298 .withExpiration(expiresAt)
299 .asTextStory(isTextStory)
300 .withBodyRanges(bodyRanges)
301 .withSharedContacts(sharedContacts);
302 }
303
304 private boolean requiresInterstitial() {
305 return stickerLocator == null &&
306 (!media.isEmpty() ||
307 !TextUtils.isEmpty(draftText) ||
308 MediaUtil.isImageOrVideoType(dataType) ||
309 (!contactSearchKeys.isEmpty() && contactSearchKeys.stream().anyMatch(key -> key.requireRecipientSearchKey().isStory())));
310 }
311
312 public static final class Builder {
313
314 private final Set<ContactSearchKey> contactSearchKeys;
315
316 private List<Media> media;
317 private String draftText;
318 private StickerLocator stickerLocator;
319 private boolean borderless;
320 private Uri dataUri;
321 private String dataType;
322 private LinkPreview linkPreview;
323 private boolean viewOnce;
324 private List<Mention> mentions;
325 private long timestamp;
326 private long expiresAt;
327 private boolean isTextStory;
328 private BodyRangeList bodyRanges;
329 private List<Contact> sharedContacts;
330
331 public Builder() {
332 this(Collections.emptySet());
333 }
334
335 public Builder(@NonNull Set<ContactSearchKey> contactSearchKeys) {
336 this.contactSearchKeys = contactSearchKeys;
337 }
338
339 public @NonNull Builder withMedia(@Nullable List<Media> media) {
340 this.media = media != null ? new ArrayList<>(media) : null;
341 return this;
342 }
343
344 public @NonNull Builder withDraftText(@Nullable String draftText) {
345 this.draftText = draftText;
346 return this;
347 }
348
349 public @NonNull Builder withStickerLocator(@Nullable StickerLocator stickerLocator) {
350 this.stickerLocator = stickerLocator;
351 return this;
352 }
353
354 public @NonNull Builder asBorderless(boolean borderless) {
355 this.borderless = borderless;
356 return this;
357 }
358
359 public @NonNull Builder withDataUri(@Nullable Uri dataUri) {
360 this.dataUri = dataUri;
361 return this;
362 }
363
364 public @NonNull Builder withDataType(@Nullable String dataType) {
365 this.dataType = dataType;
366 return this;
367 }
368
369 public @NonNull Builder withLinkPreview(@Nullable LinkPreview linkPreview) {
370 this.linkPreview = linkPreview;
371 return this;
372 }
373
374 public @NonNull Builder asViewOnce(boolean viewOnce) {
375 this.viewOnce = viewOnce;
376 return this;
377 }
378
379 public @NonNull Builder withMentions(@Nullable List<Mention> mentions) {
380 this.mentions = mentions != null ? new ArrayList<>(mentions) : null;
381 return this;
382 }
383
384 public @NonNull Builder withTimestamp(long timestamp) {
385 this.timestamp = timestamp;
386 return this;
387 }
388
389 public @NonNull Builder withExpiration(long expiresAt) {
390 this.expiresAt = expiresAt;
391 return this;
392 }
393
394 public @NonNull Builder asTextStory(boolean isTextStory) {
395 this.isTextStory = isTextStory;
396 return this;
397 }
398
399 public @NonNull Builder withBodyRanges(@Nullable BodyRangeList bodyRanges) {
400 this.bodyRanges = bodyRanges;
401 return this;
402 }
403
404 public @NonNull Builder withSharedContacts(List<Contact> sharedContacts) {
405 this.sharedContacts = new ArrayList<>(sharedContacts);
406 return this;
407 }
408
409 public @NonNull MultiShareArgs build() {
410 return new MultiShareArgs(this);
411 }
412 }
413}