That fuck shit the fascists are using
at master 487 lines 19 kB view raw
1package org.tm.archive.conversation; 2 3import android.app.Activity; 4import android.content.Context; 5import android.content.Intent; 6import android.net.Uri; 7import android.os.Bundle; 8 9import androidx.annotation.MainThread; 10import androidx.annotation.NonNull; 11import androidx.annotation.Nullable; 12 13import org.signal.core.util.logging.Log; 14import org.tm.archive.badges.models.Badge; 15import org.tm.archive.conversation.colors.ChatColors; 16import org.tm.archive.conversation.v2.ConversationActivity; 17import org.tm.archive.database.SignalDatabase; 18import org.tm.archive.database.ThreadTable; 19import org.tm.archive.mediasend.Media; 20import org.tm.archive.mms.SlideFactory; 21import org.tm.archive.recipients.Recipient; 22import org.tm.archive.recipients.RecipientId; 23import org.tm.archive.stickers.StickerLocator; 24import org.tm.archive.wallpaper.ChatWallpaper; 25import org.whispersystems.signalservice.api.util.Preconditions; 26 27import java.util.ArrayList; 28import java.util.Collection; 29import java.util.List; 30import java.util.Objects; 31 32import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; 33import io.reactivex.rxjava3.core.Single; 34import io.reactivex.rxjava3.schedulers.Schedulers; 35 36public class ConversationIntents { 37 private static final String TAG = Log.tag(ConversationIntents.class); 38 39 private static final String BUBBLE_AUTHORITY = "bubble"; 40 private static final String NOTIFICATION_CUSTOM_SCHEME = "custom"; 41 private static final String EXTRA_RECIPIENT = "recipient_id"; 42 private static final String EXTRA_THREAD_ID = "thread_id"; 43 private static final String EXTRA_TEXT = "draft_text"; 44 private static final String EXTRA_MEDIA = "media_list"; 45 private static final String EXTRA_STICKER = "sticker_extra"; 46 private static final String EXTRA_BORDERLESS = "borderless_extra"; 47 private static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type"; 48 private static final String EXTRA_STARTING_POSITION = "starting_position"; 49 private static final String EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP = "first_time_in_group"; 50 private static final String EXTRA_WITH_SEARCH_OPEN = "with_search_open"; 51 private static final String EXTRA_GIFT_BADGE = "gift_badge"; 52 private static final String EXTRA_SHARE_DATA_TIMESTAMP = "share_data_timestamp"; 53 private static final String EXTRA_CONVERSATION_TYPE = "conversation_type"; 54 private static final String INTENT_DATA = "intent_data"; 55 private static final String INTENT_TYPE = "intent_type"; 56 57 private ConversationIntents() { 58 } 59 60 /** 61 * Create a conversation builder for the given recipientId / threadId. Thread ids are required for CFV2, 62 * so we will resolve the Recipient into a ThreadId if the threadId is invalid (below 0) 63 * 64 * @param context Context for Intent creation 65 * @param recipientId The RecipientId to query the thread ID for if the passed one is invalid. 66 * @param threadId The threadId, or -1L 67 * @return A Single that will return a builder to create the conversation intent. 68 */ 69 @MainThread 70 public static @NonNull Single<Builder> createBuilder(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { 71 if (threadId > 0L) { 72 return Single.just(createBuilderSync(context, recipientId, threadId)); 73 } else { 74 return Single.fromCallable(() -> { 75 long newThreadId = SignalDatabase.threads().getOrCreateThreadIdFor(Recipient.resolved(recipientId)); 76 return createBuilderSync(context, recipientId, newThreadId); 77 }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); 78 } 79 } 80 81 public static @NonNull Builder createPopUpBuilder(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { 82 return new Builder(context, ConversationPopupActivity.class, recipientId, threadId, ConversationScreenType.POPUP); 83 } 84 85 public static @NonNull Intent createBubbleIntent(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { 86 return new Builder(context, BubbleConversationActivity.class, recipientId, threadId, ConversationScreenType.BUBBLE).build(); 87 } 88 89 /** 90 * Create a Builder for a Conversation Intent. Does not perform a lookup for the thread id if the thread id is < 1. For CFV2, this is 91 * considered an invalid state and will be met with an IllegalArgumentException. 92 * 93 * @param context Context for Intent creation 94 * @param recipientId The recipientId, only used if the threadId is not valid 95 * @param threadId The threadId, required for CFV2. 96 * @return A builder that can be used to create a conversation intent. 97 */ 98 public static @NonNull Builder createBuilderSync(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { 99 Preconditions.checkArgument(threadId > 0, "threadId is invalid"); 100 return new Builder(context, ConversationActivity.class, recipientId, threadId, ConversationScreenType.NORMAL); 101 } 102 103 static @Nullable Uri getIntentData(@NonNull Bundle bundle) { 104 return bundle.getParcelable(INTENT_DATA); 105 } 106 107 static @Nullable String getIntentType(@NonNull Bundle bundle) { 108 return bundle.getString(INTENT_TYPE); 109 } 110 111 public static @NonNull Bundle createParentFragmentArguments(@NonNull Intent intent) { 112 Bundle bundle = new Bundle(); 113 114 if (intent.getExtras() != null) { 115 bundle.putAll(intent.getExtras()); 116 } 117 118 bundle.putParcelable(INTENT_DATA, intent.getData()); 119 bundle.putString(INTENT_TYPE, intent.getType()); 120 121 return bundle; 122 } 123 124 public static boolean isBubbleIntentUri(@Nullable Uri uri) { 125 return uri != null && Objects.equals(uri.getAuthority(), BUBBLE_AUTHORITY); 126 } 127 128 static boolean isNotificationIntentUri(@Nullable Uri uri) { 129 return uri != null && Objects.equals(uri.getScheme(), NOTIFICATION_CUSTOM_SCHEME); 130 } 131 132 public final static class Args { 133 private final RecipientId recipientId; 134 private final long threadId; 135 private final String draftText; 136 private final Uri draftMedia; 137 private final String draftContentType; 138 private final SlideFactory.MediaType draftMediaType; 139 private final ArrayList<Media> media; 140 private final StickerLocator stickerLocator; 141 private final boolean isBorderless; 142 private final int distributionType; 143 private final int startingPosition; 144 private final boolean firstTimeInSelfCreatedGroup; 145 private final boolean withSearchOpen; 146 private final Badge giftBadge; 147 private final long shareDataTimestamp; 148 private final ConversationScreenType conversationScreenType; 149 150 public static Args from(@NonNull Bundle arguments) { 151 Uri intentDataUri = getIntentData(arguments); 152 if (isBubbleIntentUri(intentDataUri)) { 153 return new Args(RecipientId.from(intentDataUri.getQueryParameter(EXTRA_RECIPIENT)), 154 Long.parseLong(intentDataUri.getQueryParameter(EXTRA_THREAD_ID)), 155 null, 156 null, 157 null, 158 null, 159 null, 160 false, 161 ThreadTable.DistributionTypes.DEFAULT, 162 -1, 163 false, 164 false, 165 null, 166 -1L, 167 ConversationScreenType.BUBBLE); 168 } 169 170 return new Args(RecipientId.from(Objects.requireNonNull(arguments.getString(EXTRA_RECIPIENT))), 171 arguments.getLong(EXTRA_THREAD_ID, -1), 172 arguments.getString(EXTRA_TEXT), 173 ConversationIntents.getIntentData(arguments), 174 ConversationIntents.getIntentType(arguments), 175 arguments.getParcelableArrayList(EXTRA_MEDIA), 176 arguments.getParcelable(EXTRA_STICKER), 177 arguments.getBoolean(EXTRA_BORDERLESS, false), 178 arguments.getInt(EXTRA_DISTRIBUTION_TYPE, ThreadTable.DistributionTypes.DEFAULT), 179 arguments.getInt(EXTRA_STARTING_POSITION, -1), 180 arguments.getBoolean(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, false), 181 arguments.getBoolean(EXTRA_WITH_SEARCH_OPEN, false), 182 arguments.getParcelable(EXTRA_GIFT_BADGE), 183 arguments.getLong(EXTRA_SHARE_DATA_TIMESTAMP, -1L), 184 ConversationScreenType.from(arguments.getInt(EXTRA_CONVERSATION_TYPE, 0))); 185 } 186 187 private Args(@NonNull RecipientId recipientId, 188 long threadId, 189 @Nullable String draftText, 190 @Nullable Uri draftMedia, 191 @Nullable String draftContentType, 192 @Nullable ArrayList<Media> media, 193 @Nullable StickerLocator stickerLocator, 194 boolean isBorderless, 195 int distributionType, 196 int startingPosition, 197 boolean firstTimeInSelfCreatedGroup, 198 boolean withSearchOpen, 199 @Nullable Badge giftBadge, 200 long shareDataTimestamp, 201 @NonNull ConversationScreenType conversationScreenType) 202 { 203 this.recipientId = recipientId; 204 this.threadId = threadId; 205 this.draftText = draftText; 206 this.draftMedia = draftMedia; 207 this.draftContentType = draftContentType; 208 this.media = media; 209 this.stickerLocator = stickerLocator; 210 this.isBorderless = isBorderless; 211 this.distributionType = distributionType; 212 this.startingPosition = startingPosition; 213 this.firstTimeInSelfCreatedGroup = firstTimeInSelfCreatedGroup; 214 this.withSearchOpen = withSearchOpen; 215 this.giftBadge = giftBadge; 216 this.shareDataTimestamp = shareDataTimestamp; 217 this.conversationScreenType = conversationScreenType; 218 this.draftMediaType = SlideFactory.MediaType.from(draftContentType); 219 } 220 221 public @NonNull RecipientId getRecipientId() { 222 return recipientId; 223 } 224 225 public long getThreadId() { 226 return threadId; 227 } 228 229 public @Nullable String getDraftText() { 230 return draftText; 231 } 232 233 public @Nullable Uri getDraftMedia() { 234 return draftMedia; 235 } 236 237 public @Nullable String getDraftContentType() { 238 return draftContentType; 239 } 240 241 public @Nullable SlideFactory.MediaType getDraftMediaType() { 242 return draftMediaType; 243 } 244 245 public @Nullable ArrayList<Media> getMedia() { 246 return media; 247 } 248 249 public @Nullable StickerLocator getStickerLocator() { 250 return stickerLocator; 251 } 252 253 public int getDistributionType() { 254 return distributionType; 255 } 256 257 public int getStartingPosition() { 258 return startingPosition; 259 } 260 261 public boolean isBorderless() { 262 return isBorderless; 263 } 264 265 public boolean isFirstTimeInSelfCreatedGroup() { 266 return firstTimeInSelfCreatedGroup; 267 } 268 269 public @Nullable ChatWallpaper getWallpaper() { 270 return Recipient.resolved(recipientId).getWallpaper(); 271 } 272 273 public @NonNull ChatColors getChatColors() { 274 return Recipient.resolved(recipientId).getChatColors(); 275 } 276 277 public boolean isWithSearchOpen() { 278 return withSearchOpen; 279 } 280 281 public @Nullable Badge getGiftBadge() { 282 return giftBadge; 283 } 284 285 public long getShareDataTimestamp() { 286 return shareDataTimestamp; 287 } 288 289 public @NonNull ConversationScreenType getConversationScreenType() { 290 return conversationScreenType; 291 } 292 293 public boolean canInitializeFromDatabase() { 294 return draftText == null && (draftMedia == null || ConversationIntents.isBubbleIntentUri(draftMedia) || ConversationIntents.isNotificationIntentUri(draftMedia)) && draftMediaType == null; 295 } 296 } 297 298 public final static class Builder { 299 private final Context context; 300 private final Class<? extends Activity> conversationActivityClass; 301 private final RecipientId recipientId; 302 private final long threadId; 303 private final ConversationScreenType conversationScreenType; 304 305 private String draftText; 306 private List<Media> media; 307 private StickerLocator stickerLocator; 308 private boolean isBorderless; 309 private int distributionType = ThreadTable.DistributionTypes.DEFAULT; 310 private int startingPosition = -1; 311 private Uri dataUri; 312 private String dataType; 313 private boolean firstTimeInSelfCreatedGroup; 314 private boolean withSearchOpen; 315 private Badge giftBadge; 316 private long shareDataTimestamp = -1L; 317 318 private Builder(@NonNull Context context, 319 @NonNull Class<? extends Activity> conversationActivityClass, 320 @NonNull RecipientId recipientId, 321 long threadId, 322 @NonNull ConversationScreenType conversationScreenType) 323 { 324 this.context = context; 325 this.conversationActivityClass = conversationActivityClass; 326 this.recipientId = recipientId; 327 this.threadId = checkThreadId(threadId); 328 this.conversationScreenType = conversationScreenType; 329 } 330 331 public @NonNull Builder withDraftText(@Nullable String draftText) { 332 this.draftText = draftText; 333 return this; 334 } 335 336 public @NonNull Builder withMedia(@Nullable Collection<Media> media) { 337 this.media = media != null ? new ArrayList<>(media) : null; 338 return this; 339 } 340 341 public @NonNull Builder withStickerLocator(@Nullable StickerLocator stickerLocator) { 342 this.stickerLocator = stickerLocator; 343 return this; 344 } 345 346 public @NonNull Builder asBorderless(boolean isBorderless) { 347 this.isBorderless = isBorderless; 348 return this; 349 } 350 351 public @NonNull Builder withDistributionType(int distributionType) { 352 this.distributionType = distributionType; 353 return this; 354 } 355 356 public @NonNull Builder withStartingPosition(int startingPosition) { 357 this.startingPosition = startingPosition; 358 return this; 359 } 360 361 public @NonNull Builder withDataUri(@Nullable Uri dataUri) { 362 this.dataUri = dataUri; 363 return this; 364 } 365 366 public @NonNull Builder withDataType(@Nullable String dataType) { 367 this.dataType = dataType; 368 return this; 369 } 370 371 public @NonNull Builder withSearchOpen(boolean withSearchOpen) { 372 this.withSearchOpen = withSearchOpen; 373 return this; 374 } 375 376 public Builder firstTimeInSelfCreatedGroup() { 377 this.firstTimeInSelfCreatedGroup = true; 378 return this; 379 } 380 381 public Builder withGiftBadge(@NonNull Badge badge) { 382 this.giftBadge = badge; 383 return this; 384 } 385 386 public Builder withShareDataTimestamp(long timestamp) { 387 this.shareDataTimestamp = timestamp; 388 return this; 389 } 390 391 public @NonNull Intent build() { 392 if (stickerLocator != null && media != null) { 393 throw new IllegalStateException("Cannot have both sticker and media array"); 394 } 395 396 Intent intent = new Intent(context, conversationActivityClass); 397 398 intent.setAction(Intent.ACTION_DEFAULT); 399 400 if (conversationScreenType.isInBubble()) { 401 intent.setData(new Uri.Builder().authority(BUBBLE_AUTHORITY) 402 .appendQueryParameter(EXTRA_RECIPIENT, recipientId.serialize()) 403 .appendQueryParameter(EXTRA_THREAD_ID, String.valueOf(threadId)) 404 .build()); 405 406 return intent; 407 } 408 409 intent.putExtra(EXTRA_RECIPIENT, recipientId.serialize()); 410 intent.putExtra(EXTRA_THREAD_ID, threadId); 411 intent.putExtra(EXTRA_DISTRIBUTION_TYPE, distributionType); 412 intent.putExtra(EXTRA_STARTING_POSITION, startingPosition); 413 intent.putExtra(EXTRA_BORDERLESS, isBorderless); 414 intent.putExtra(EXTRA_FIRST_TIME_IN_SELF_CREATED_GROUP, firstTimeInSelfCreatedGroup); 415 intent.putExtra(EXTRA_WITH_SEARCH_OPEN, withSearchOpen); 416 intent.putExtra(EXTRA_GIFT_BADGE, giftBadge); 417 intent.putExtra(EXTRA_SHARE_DATA_TIMESTAMP, shareDataTimestamp); 418 intent.putExtra(EXTRA_CONVERSATION_TYPE, conversationScreenType.code); 419 420 if (draftText != null) { 421 intent.putExtra(EXTRA_TEXT, draftText); 422 } 423 424 if (media != null) { 425 intent.putParcelableArrayListExtra(EXTRA_MEDIA, new ArrayList<>(media)); 426 } 427 428 if (stickerLocator != null) { 429 intent.putExtra(EXTRA_STICKER, stickerLocator); 430 } 431 432 if (dataUri != null && dataType != null) { 433 intent.setDataAndType(dataUri, dataType); 434 } else if (dataUri != null) { 435 intent.setData(dataUri); 436 } else if (dataType != null) { 437 intent.setType(dataType); 438 } 439 440 Bundle args = ConversationIntents.createParentFragmentArguments(intent); 441 442 return intent.putExtras(args); 443 } 444 } 445 446 public enum ConversationScreenType { 447 NORMAL(0), 448 BUBBLE(1), 449 POPUP(2); 450 451 private final int code; 452 453 ConversationScreenType(int code) { 454 this.code = code; 455 } 456 457 public boolean isInBubble() { 458 return Objects.equals(this, BUBBLE); 459 } 460 461 public boolean isInPopup() { 462 return Objects.equals(this, POPUP); 463 } 464 465 public boolean isNormal() { 466 return Objects.equals(this, NORMAL); 467 } 468 469 private static @NonNull ConversationScreenType from(int code) { 470 for (ConversationScreenType type : values()) { 471 if (type.code == code) { 472 return type; 473 } 474 } 475 476 return NORMAL; 477 } 478 } 479 480 private static long checkThreadId(long threadId) { 481 if (threadId < 0) { 482 throw new IllegalArgumentException("ThreadId is a required field in CFV2"); 483 } else { 484 return threadId; 485 } 486 } 487}