That fuck shit the fascists are using
at master 746 lines 31 kB view raw
1package org.tm.archive.conversation; 2 3import android.content.Context; 4import android.content.res.ColorStateList; 5import android.text.Spannable; 6import android.text.SpannableString; 7import android.text.method.LinkMovementMethod; 8import android.util.AttributeSet; 9import android.view.View; 10import android.view.ViewGroup; 11import android.widget.FrameLayout; 12import android.widget.TextView; 13 14import androidx.annotation.NonNull; 15import androidx.annotation.Nullable; 16import androidx.appcompat.content.res.AppCompatResources; 17import androidx.core.content.ContextCompat; 18import androidx.lifecycle.LifecycleOwner; 19import androidx.lifecycle.LiveData; 20import androidx.lifecycle.Observer; 21import androidx.lifecycle.Transformations; 22 23import com.bumptech.glide.RequestManager; 24import com.google.android.material.button.MaterialButton; 25import com.google.common.collect.Sets; 26 27import org.signal.core.util.concurrent.ListenableFuture; 28import org.signal.core.util.logging.Log; 29import org.tm.archive.BindableConversationItem; 30import org.tm.archive.R; 31import org.tm.archive.conversation.colors.Colorizer; 32import org.tm.archive.conversation.mutiselect.MultiselectPart; 33import org.tm.archive.conversation.ui.error.EnableCallNotificationSettingsDialog; 34import org.tm.archive.database.model.GroupCallUpdateDetailsUtil; 35import org.tm.archive.database.model.IdentityRecord; 36import org.tm.archive.database.model.InMemoryMessageRecord; 37import org.tm.archive.database.model.LiveUpdateMessage; 38import org.tm.archive.database.model.MessageRecord; 39import org.tm.archive.database.model.UpdateDescription; 40import org.tm.archive.groups.LiveGroup; 41import org.tm.archive.keyvalue.SignalStore; 42import org.tm.archive.recipients.LiveRecipient; 43import org.tm.archive.recipients.Recipient; 44import org.tm.archive.util.DateUtils; 45import org.tm.archive.util.IdentityUtil; 46import org.tm.archive.util.Projection; 47import org.tm.archive.util.ProjectionList; 48import org.tm.archive.util.ThemeUtil; 49import org.tm.archive.util.Util; 50import org.tm.archive.util.ViewUtil; 51import org.tm.archive.util.livedata.LiveDataUtil; 52import org.tm.archive.verify.VerifyIdentityActivity; 53import org.whispersystems.signalservice.api.push.ServiceId; 54import org.whispersystems.signalservice.api.push.ServiceId.ACI; 55 56import java.util.Collection; 57import java.util.Locale; 58import java.util.Objects; 59import java.util.Optional; 60import java.util.Set; 61import java.util.UUID; 62import java.util.concurrent.ExecutionException; 63import java.util.stream.Collectors; 64 65public final class ConversationUpdateItem extends FrameLayout 66 implements BindableConversationItem 67{ 68 private static final String TAG = Log.tag(ConversationUpdateItem.class); 69 private static final ProjectionList EMPTY_PROJECTION_LIST = new ProjectionList(); 70 71 72 private Set<MultiselectPart> batchSelected; 73 74 private TextView body; 75 private MaterialButton actionButton; 76 private View background; 77 private ConversationMessage conversationMessage; 78 private Recipient conversationRecipient; 79 private Optional<MessageRecord> previousMessageRecord; 80 private Optional<MessageRecord> nextMessageRecord; 81 private MessageRecord messageRecord; 82 private boolean isMessageRequestAccepted; 83 private LiveData<SpannableString> displayBody; 84 private EventListener eventListener; 85 86 private final UpdateObserver updateObserver = new UpdateObserver(); 87 88 private final PresentOnChange presentOnChange = new PresentOnChange(); 89 private final RecipientObserverManager senderObserver = new RecipientObserverManager(presentOnChange); 90 private final RecipientObserverManager groupObserver = new RecipientObserverManager(presentOnChange); 91 private final GroupDataManager groupData = new GroupDataManager(); 92 93 public ConversationUpdateItem(Context context) { 94 super(context); 95 } 96 97 public ConversationUpdateItem(Context context, AttributeSet attrs) { 98 super(context, attrs); 99 } 100 101 @Override 102 public void onFinishInflate() { 103 super.onFinishInflate(); 104 this.body = findViewById(R.id.conversation_update_body); 105 this.actionButton = findViewById(R.id.conversation_update_action); 106 this.background = findViewById(R.id.conversation_update_background); 107 108 body.setOnClickListener(v -> performClick()); 109 body.setOnLongClickListener(v -> performLongClick()); 110 111 this.setOnClickListener(new InternalClickListener(null)); 112 } 113 114 @Override 115 public void bind(@NonNull LifecycleOwner lifecycleOwner, 116 @NonNull ConversationMessage conversationMessage, 117 @NonNull Optional<MessageRecord> previousMessageRecord, 118 @NonNull Optional<MessageRecord> nextMessageRecord, 119 @NonNull RequestManager requestManager, 120 @NonNull Locale locale, 121 @NonNull Set<MultiselectPart> batchSelected, 122 @NonNull Recipient conversationRecipient, 123 @Nullable String searchQuery, 124 boolean pulseMention, 125 boolean hasWallpaper, 126 boolean isMessageRequestAccepted, 127 boolean allowedToPlayInline, 128 @NonNull Colorizer colorizer, 129 @NonNull ConversationItemDisplayMode displayMode) 130 { 131 this.batchSelected = batchSelected; 132 133 bind(lifecycleOwner, conversationMessage, previousMessageRecord, nextMessageRecord, conversationRecipient, hasWallpaper, isMessageRequestAccepted); 134 } 135 136 @Override 137 public void setEventListener(@Nullable EventListener listener) { 138 this.eventListener = listener; 139 } 140 141 @Override 142 public @NonNull ConversationMessage getConversationMessage() { 143 return conversationMessage; 144 } 145 146 private void bind(@NonNull LifecycleOwner lifecycleOwner, 147 @NonNull ConversationMessage conversationMessage, 148 @NonNull Optional<MessageRecord> previousMessageRecord, 149 @NonNull Optional<MessageRecord> nextMessageRecord, 150 @NonNull Recipient conversationRecipient, 151 boolean hasWallpaper, 152 boolean isMessageRequestAccepted) 153 { 154 this.conversationMessage = conversationMessage; 155 this.messageRecord = conversationMessage.getMessageRecord(); 156 this.previousMessageRecord = previousMessageRecord; 157 this.nextMessageRecord = nextMessageRecord; 158 this.conversationRecipient = conversationRecipient; 159 this.isMessageRequestAccepted = isMessageRequestAccepted; 160 161 senderObserver.observe(lifecycleOwner, messageRecord.getFromRecipient()); 162 163 if (conversationRecipient.isActiveGroup() && 164 (messageRecord.isGroupCall() || messageRecord.isCollapsedGroupV2JoinUpdate() || messageRecord.isGroupV2JoinRequest(messageRecord.getFromRecipient().getServiceId().orElse(null)))) { 165 groupObserver.observe(lifecycleOwner, conversationRecipient); 166 groupData.observe(lifecycleOwner, conversationRecipient); 167 } else { 168 groupObserver.observe(lifecycleOwner, null); 169 } 170 171 int textColor = ContextCompat.getColor(getContext(), R.color.conversation_item_update_text_color); 172 if (ThemeUtil.isDarkTheme(getContext()) && hasWallpaper) { 173 textColor = ContextCompat.getColor(getContext(), R.color.core_grey_15); 174 } 175 176 UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext(), eventListener::onRecipientNameClicked)); 177 LiveData<SpannableString> liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(getContext(), updateDescription, textColor, true); 178 LiveData<SpannableString> spannableMessage = loading(liveUpdateMessage); 179 180 observeDisplayBody(lifecycleOwner, spannableMessage); 181 182 present(conversationMessage, nextMessageRecord, conversationRecipient, isMessageRequestAccepted); 183 184 presentBackground(shouldCollapse(messageRecord, previousMessageRecord), 185 shouldCollapse(messageRecord, nextMessageRecord), 186 hasWallpaper); 187 188 presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isBoostRequest()); 189 190 updateSelectedState(); 191 } 192 193 @Override 194 public void updateSelectedState() { 195 if (batchSelected.size() > 0) { 196 body.setMovementMethod(null); 197 } else { 198 body.setMovementMethod(LinkMovementMethod.getInstance()); 199 } 200 } 201 202 private static boolean shouldCollapse(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> candidate) 203 { 204 return candidate.isPresent() && 205 candidate.get().isUpdate() && 206 DateUtils.isSameDay(current.getTimestamp(), candidate.get().getTimestamp()) && 207 isSameType(current, candidate.get()); 208 } 209 210 /** After a short delay, if the main data hasn't shown yet, then a loading message is displayed. */ 211 private @NonNull LiveData<SpannableString> loading(@NonNull LiveData<SpannableString> string) { 212 return LiveDataUtil.until(string, LiveDataUtil.delay(250, new SpannableString(getContext().getString(R.string.ConversationUpdateItem_loading)))); 213 } 214 215 @Override 216 public void unbind() { 217 } 218 219 @Override 220 public void showProjectionArea() { 221 } 222 223 @Override 224 public void hideProjectionArea() { 225 throw new UnsupportedOperationException("Call makes no sense for a conversation update item"); 226 } 227 228 @Override 229 public int getAdapterPosition() { 230 throw new UnsupportedOperationException("Don't delegate to this method."); 231 } 232 233 @Override 234 public @NonNull Projection getGiphyMp4PlayableProjection(@NonNull ViewGroup recyclerView) { 235 throw new UnsupportedOperationException("ConversationUpdateItems cannot be projected into."); 236 } 237 238 @Override 239 public boolean canPlayContent() { 240 return false; 241 } 242 243 @Override 244 public boolean shouldProjectContent() { 245 return false; 246 } 247 248 @Override 249 public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) { 250 return EMPTY_PROJECTION_LIST; 251 } 252 253 @Override 254 public @Nullable View getHorizontalTranslationTarget() { 255 return background; 256 } 257 258 @Override 259 public @NonNull ViewGroup getRoot() { 260 return this; 261 } 262 263 static final class RecipientObserverManager { 264 265 private final Observer<Recipient> recipientObserver; 266 267 private LiveRecipient recipient; 268 269 RecipientObserverManager(@NonNull Observer<Recipient> observer){ 270 this.recipientObserver = observer; 271 } 272 273 public void observe(@NonNull LifecycleOwner lifecycleOwner, @Nullable Recipient recipient) { 274 if (this.recipient != null) { 275 this.recipient.getLiveData().removeObserver(recipientObserver); 276 } 277 278 if (recipient != null) { 279 this.recipient = recipient.live(); 280 this.recipient.getLiveData().observe(lifecycleOwner, recipientObserver); 281 } else { 282 this.recipient = null; 283 } 284 } 285 286 @NonNull Recipient getObservedRecipient() { 287 return recipient.get(); 288 } 289 } 290 291 private final class GroupDataManager { 292 293 private final Observer<Object> updater; 294 295 private LiveGroup liveGroup; 296 private LiveData<Boolean> liveIsSelfAdmin; 297 private LiveData<Set<ServiceId>> liveBannedMembers; 298 private LiveData<Set<UUID>> liveFullMembers; 299 private Recipient conversationRecipient; 300 301 GroupDataManager() { 302 this.updater = unused -> update(); 303 } 304 305 public void observe(@NonNull LifecycleOwner lifecycleOwner, @Nullable Recipient recipient) { 306 if (liveGroup != null) { 307 liveIsSelfAdmin.removeObserver(updater); 308 liveBannedMembers.removeObserver(updater); 309 liveFullMembers.removeObserver(updater); 310 } 311 312 if (recipient != null) { 313 conversationRecipient = recipient; 314 liveGroup = new LiveGroup(recipient.requireGroupId()); 315 liveIsSelfAdmin = liveGroup.isSelfAdmin(); 316 liveBannedMembers = liveGroup.getBannedMembers(); 317 liveFullMembers = Transformations.map(liveGroup.getFullMembers(), 318 members -> members.stream() 319 .map(m -> m.getMember().requireAci().getRawUuid()) 320 .collect(Collectors.toSet())); 321 322 liveIsSelfAdmin.observe(lifecycleOwner, updater); 323 liveBannedMembers.observe(lifecycleOwner, updater); 324 liveFullMembers.observe(lifecycleOwner, updater); 325 } else { 326 conversationRecipient = null; 327 liveGroup = null; 328 liveBannedMembers = null; 329 liveIsSelfAdmin = null; 330 } 331 } 332 333 public boolean isSelfAdmin() { 334 if (liveIsSelfAdmin == null) { 335 return false; 336 } 337 return liveIsSelfAdmin.getValue() != null ? liveIsSelfAdmin.getValue() : false; 338 } 339 340 public boolean isBanned(Recipient recipient) { 341 if (liveBannedMembers == null) { 342 return false; 343 } 344 345 Set<ServiceId> bannedMembers = liveBannedMembers.getValue(); 346 if (bannedMembers != null) { 347 return recipient.getServiceId().isPresent() && bannedMembers.contains(recipient.requireServiceId()); 348 } 349 return false; 350 } 351 352 public boolean isFullMember(Recipient recipient) { 353 if (liveFullMembers == null) { 354 return false; 355 } 356 357 Set<UUID> members = liveFullMembers.getValue(); 358 if (members != null) { 359 return recipient.hasAci() && members.contains(recipient.requireAci().getRawUuid()); 360 } 361 return false; 362 } 363 364 private void update() { 365 present(conversationMessage, nextMessageRecord, conversationRecipient, isMessageRequestAccepted); 366 } 367 } 368 369 @Override 370 public @NonNull MultiselectPart getMultiselectPartForLatestTouch() { 371 return conversationMessage.getMultiselectCollection().asSingle().getSinglePart(); 372 } 373 374 @Override 375 public int getTopBoundaryOfMultiselectPart(@NonNull MultiselectPart multiselectPart) { 376 return getTop(); 377 } 378 379 @Override 380 public int getBottomBoundaryOfMultiselectPart(@NonNull MultiselectPart multiselectPart) { 381 return getBottom(); 382 } 383 384 @Override 385 public boolean hasNonSelectableMedia() { 386 return false; 387 } 388 389 private void observeDisplayBody(@NonNull LifecycleOwner lifecycleOwner, @Nullable LiveData<SpannableString> displayBody) { 390 if (this.displayBody != displayBody) { 391 if (this.displayBody != null) { 392 this.displayBody.removeObserver(updateObserver); 393 } 394 395 this.displayBody = displayBody; 396 397 if (this.displayBody != null) { 398 this.displayBody.observe(lifecycleOwner, updateObserver); 399 } 400 } 401 } 402 403 private void setBodyText(@Nullable CharSequence text) { 404 if (text == null) { 405 body.setVisibility(INVISIBLE); 406 } else { 407 body.setText(text); 408 body.setVisibility(VISIBLE); 409 } 410 } 411 412 private void present(@NonNull ConversationMessage conversationMessage, 413 @NonNull Optional<MessageRecord> nextMessageRecord, 414 @NonNull Recipient conversationRecipient, 415 boolean isMessageRequestAccepted) 416 { 417 Set<MultiselectPart> multiselectParts = conversationMessage.getMultiselectCollection().toSet(); 418 419 setSelected(!Sets.intersection(multiselectParts, batchSelected).isEmpty()); 420 421 if (conversationMessage.getMessageRecord().isGroupV1MigrationEvent() && 422 (!nextMessageRecord.isPresent() || !nextMessageRecord.get().isGroupV1MigrationEvent())) 423 { 424 actionButton.setText(R.string.ConversationUpdateItem_learn_more); 425 actionButton.setVisibility(VISIBLE); 426 actionButton.setOnClickListener(v -> { 427 if (batchSelected.isEmpty() && eventListener != null) { 428 eventListener.onGroupMigrationLearnMoreClicked(conversationMessage.getMessageRecord().getGroupV1MigrationMembershipChanges()); 429 } 430 }); 431 } else if (conversationMessage.getMessageRecord().isChatSessionRefresh() && 432 (!nextMessageRecord.isPresent() || !nextMessageRecord.get().isChatSessionRefresh())) 433 { 434 actionButton.setText(R.string.ConversationUpdateItem_learn_more); 435 actionButton.setVisibility(VISIBLE); 436 actionButton.setOnClickListener(v -> { 437 if (batchSelected.isEmpty() && eventListener != null) { 438 eventListener.onChatSessionRefreshLearnMoreClicked(); 439 } 440 }); 441 } else if (conversationMessage.getMessageRecord().isIdentityUpdate()) { 442 actionButton.setText(R.string.ConversationUpdateItem_learn_more); 443 actionButton.setVisibility(VISIBLE); 444 actionButton.setOnClickListener(v -> { 445 if (batchSelected.isEmpty() && eventListener != null) { 446 eventListener.onSafetyNumberLearnMoreClicked(conversationMessage.getMessageRecord().getFromRecipient()); 447 } 448 }); 449 } else if (conversationMessage.getMessageRecord().isGroupCall()) { 450 UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true); 451 Collection<ACI> acis = updateDescription.getMentioned(); 452 453 int text = 0; 454 if (Util.hasItems(acis)) { 455 if (acis.contains(SignalStore.account().requireAci())) { 456 text = R.string.ConversationUpdateItem_return_to_call; 457 } else if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).isCallFull) { 458 text = R.string.ConversationUpdateItem_call_is_full; 459 } else { 460 text = R.string.ConversationUpdateItem_join_call; 461 } 462 } 463 464 if (text != 0 && conversationRecipient.isGroup() && conversationRecipient.isActiveGroup()) { 465 actionButton.setText(text); 466 actionButton.setVisibility(VISIBLE); 467 actionButton.setOnClickListener(v -> { 468 if (batchSelected.isEmpty() && eventListener != null) { 469 eventListener.onJoinGroupCallClicked(); 470 } 471 }); 472 } else { 473 actionButton.setVisibility(GONE); 474 actionButton.setOnClickListener(null); 475 } 476 } else if (conversationMessage.getMessageRecord().isSelfCreatedGroup()) { 477 actionButton.setText(R.string.ConversationUpdateItem_invite_friends); 478 actionButton.setVisibility(VISIBLE); 479 actionButton.setOnClickListener(v -> { 480 if (batchSelected.isEmpty() && eventListener != null) { 481 eventListener.onInviteFriendsToGroupClicked(conversationRecipient.requireGroupId().requireV2()); 482 } 483 }); 484 } else if ((conversationMessage.getMessageRecord().isMissedAudioCall() || conversationMessage.getMessageRecord().isMissedVideoCall()) && EnableCallNotificationSettingsDialog.shouldShow(getContext())) { 485 actionButton.setVisibility(VISIBLE); 486 actionButton.setText(R.string.ConversationUpdateItem_enable_call_notifications); 487 actionButton.setOnClickListener(v -> { 488 if (eventListener != null) { 489 eventListener.onEnableCallNotificationsClicked(); 490 } 491 }); 492 } else if (conversationMessage.getMessageRecord().isInMemoryMessageRecord() && ((InMemoryMessageRecord) conversationMessage.getMessageRecord()).showActionButton()) { 493 InMemoryMessageRecord inMemoryMessageRecord = (InMemoryMessageRecord) conversationMessage.getMessageRecord(); 494 actionButton.setVisibility(VISIBLE); 495 actionButton.setText(inMemoryMessageRecord.getActionButtonText()); 496 actionButton.setOnClickListener(v -> { 497 if (eventListener != null) { 498 eventListener.onInMemoryMessageClicked(inMemoryMessageRecord); 499 } 500 }); 501 } else if (conversationMessage.getMessageRecord().isGroupV2DescriptionUpdate()) { 502 actionButton.setVisibility(VISIBLE); 503 actionButton.setText(R.string.ConversationUpdateItem_view); 504 actionButton.setOnClickListener(v -> { 505 if (eventListener != null) { 506 eventListener.onViewGroupDescriptionChange(conversationRecipient.getGroupId().orElse(null), conversationMessage.getMessageRecord().getGroupV2DescriptionUpdate(), isMessageRequestAccepted); 507 } 508 }); 509 } else if (conversationMessage.getMessageRecord().isBadDecryptType() && 510 (!nextMessageRecord.isPresent() || !nextMessageRecord.get().isBadDecryptType())) 511 { 512 actionButton.setText(R.string.ConversationUpdateItem_learn_more); 513 actionButton.setVisibility(VISIBLE); 514 actionButton.setOnClickListener(v -> { 515 if (batchSelected.isEmpty() && eventListener != null) { 516 eventListener.onBadDecryptLearnMoreClicked(conversationMessage.getMessageRecord().getFromRecipient().getId()); 517 } 518 }); 519 } else if (conversationMessage.getMessageRecord().isChangeNumber() && conversationMessage.getMessageRecord().getFromRecipient().isSystemContact()) { 520 actionButton.setText(R.string.ConversationUpdateItem_update_contact); 521 actionButton.setVisibility(VISIBLE); 522 actionButton.setOnClickListener(v -> { 523 if (batchSelected.isEmpty() && eventListener != null) { 524 eventListener.onChangeNumberUpdateContact(conversationMessage.getMessageRecord().getFromRecipient()); 525 } 526 }); 527 } else if (shouldShowBlockRequestAction(conversationMessage.getMessageRecord())) { 528 actionButton.setText(R.string.ConversationUpdateItem_block_request); 529 actionButton.setVisibility(VISIBLE); 530 actionButton.setOnClickListener(v -> { 531 if (batchSelected.isEmpty() && eventListener != null) { 532 eventListener.onBlockJoinRequest(conversationMessage.getMessageRecord().getFromRecipient()); 533 } 534 }); 535 } else if (conversationMessage.getMessageRecord().isBoostRequest()) { 536 actionButton.setVisibility(VISIBLE); 537 actionButton.setOnClickListener(v -> { 538 if (batchSelected.isEmpty() && eventListener != null) { 539 eventListener.onDonateClicked(); 540 } 541 }); 542 543 actionButton.setText(R.string.ConversationUpdateItem_donate); 544 } else if (conversationMessage.getMessageRecord().isSmsExportType()) { 545 actionButton.setVisibility(View.VISIBLE); 546 actionButton.setOnClickListener(v -> { 547 if (batchSelected.isEmpty() && eventListener != null) { 548 eventListener.onInviteToSignalClicked(); 549 } 550 }); 551 552 actionButton.setText(R.string.ConversationActivity__invite_to_signal); 553 } else if (conversationMessage.getMessageRecord().isPaymentsRequestToActivate() && !conversationMessage.getMessageRecord().isOutgoing() && !SignalStore.paymentsValues().mobileCoinPaymentsEnabled()) { 554 actionButton.setText(R.string.ConversationUpdateItem_activate_payments); 555 actionButton.setVisibility(VISIBLE); 556 actionButton.setOnClickListener(v -> { 557 if (batchSelected.isEmpty() && eventListener != null) { 558 eventListener.onActivatePaymentsClicked(); 559 } 560 }); 561 } else if (conversationMessage.getMessageRecord().isPaymentsActivated() && !conversationMessage.getMessageRecord().isOutgoing()) { 562 actionButton.setText(R.string.ConversationUpdateItem_send_payment); 563 actionButton.setVisibility(VISIBLE); 564 actionButton.setOnClickListener(v -> { 565 if (batchSelected.isEmpty() && eventListener != null) { 566 eventListener.onSendPaymentClicked(conversationMessage.getMessageRecord().getFromRecipient().getId()); 567 } 568 }); 569 } else if (conversationMessage.getMessageRecord().isReportedSpam()) { 570 actionButton.setText(R.string.ConversationUpdateItem_learn_more); 571 actionButton.setVisibility(VISIBLE); 572 actionButton.setOnClickListener(v -> { 573 if (batchSelected.isEmpty() && eventListener != null) { 574 eventListener.onReportSpamLearnMoreClicked(); 575 } 576 }); 577 } else if (conversationMessage.getMessageRecord().isMessageRequestAccepted()) { 578 actionButton.setText(R.string.ConversationUpdateItem_options); 579 actionButton.setVisibility(VISIBLE); 580 actionButton.setOnClickListener(v -> { 581 if (batchSelected.isEmpty() && eventListener != null) { 582 eventListener.onMessageRequestAcceptOptionsClicked(); 583 } 584 }); 585 } else{ 586 actionButton.setVisibility(GONE); 587 actionButton.setOnClickListener(null); 588 } 589 } 590 591 private boolean shouldShowBlockRequestAction(MessageRecord messageRecord) { 592 Recipient toBlock = messageRecord.getFromRecipient(); 593 594 if (!toBlock.hasServiceId() || !groupData.isSelfAdmin() || groupData.isBanned(toBlock) || groupData.isFullMember(toBlock)) { 595 return false; 596 } 597 598 return (messageRecord.isCollapsedGroupV2JoinUpdate() && !nextMessageRecord.map(m -> m.isGroupV2JoinRequest(toBlock.requireServiceId())).orElse(false)) || 599 (messageRecord.isGroupV2JoinRequest(toBlock.requireServiceId()) && previousMessageRecord.map(m -> m.isCollapsedGroupV2JoinUpdate(toBlock.requireServiceId())).orElse(false)); 600 } 601 602 private void presentBackground(boolean collapseAbove, boolean collapseBelow, boolean hasWallpaper) { 603 int marginDefault = getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_update_vertical_margin); 604 int marginCollapsed = 0; 605 int paddingDefault = getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_update_vertical_padding); 606 int paddingCollapsed = getContext().getResources().getDimensionPixelOffset(R.dimen.conversation_update_vertical_padding_collapsed); 607 608 if (collapseAbove && collapseBelow) { 609 ViewUtil.setTopMargin(background, marginCollapsed); 610 ViewUtil.setBottomMargin(background, marginCollapsed); 611 612 ViewUtil.setPaddingTop(background, paddingCollapsed); 613 ViewUtil.setPaddingBottom(background, paddingCollapsed); 614 615 ViewUtil.updateLayoutParams(background, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 616 617 if (hasWallpaper) { 618 background.setBackgroundResource(R.drawable.conversation_update_wallpaper_background_middle); 619 } else { 620 background.setBackground(null); 621 } 622 } else if (collapseAbove) { 623 ViewUtil.setTopMargin(background, marginCollapsed); 624 ViewUtil.setBottomMargin(background, marginDefault); 625 626 ViewUtil.setPaddingTop(background, paddingDefault); 627 ViewUtil.setPaddingBottom(background, paddingDefault); 628 629 ViewUtil.updateLayoutParams(background, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 630 631 if (hasWallpaper) { 632 background.setBackgroundResource(R.drawable.conversation_update_wallpaper_background_bottom); 633 } else { 634 background.setBackground(null); 635 } 636 } else if (collapseBelow) { 637 ViewUtil.setTopMargin(background, marginDefault); 638 ViewUtil.setBottomMargin(background, marginCollapsed); 639 640 ViewUtil.setPaddingTop(background, paddingDefault); 641 ViewUtil.setPaddingBottom(background, paddingCollapsed); 642 643 ViewUtil.updateLayoutParams(background, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); 644 645 if (hasWallpaper) { 646 background.setBackgroundResource(R.drawable.conversation_update_wallpaper_background_top); 647 } else { 648 background.setBackground(null); 649 } 650 } else { 651 ViewUtil.setTopMargin(background, marginDefault); 652 ViewUtil.setBottomMargin(background, marginDefault); 653 654 ViewUtil.setPaddingTop(background, paddingDefault); 655 ViewUtil.setPaddingBottom(background, paddingDefault); 656 657 ViewUtil.updateLayoutParams(background, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 658 659 if (hasWallpaper) { 660 background.setBackgroundResource(R.drawable.conversation_update_wallpaper_background_singular); 661 } else { 662 background.setBackground(null); 663 } 664 } 665 } 666 667 private void presentActionButton(boolean hasWallpaper, boolean isBoostRequest) { 668 if (isBoostRequest) { 669 actionButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(getContext(), R.color.signal_colorSecondaryContainer))); 670 actionButton.setTextColor(ColorStateList.valueOf(ContextCompat.getColor(getContext(), R.color.signal_colorOnSecondaryContainer))); 671 } else if (hasWallpaper) { 672 actionButton.setBackgroundTintList(AppCompatResources.getColorStateList(getContext(), R.color.conversation_update_item_button_background_wallpaper)); 673 actionButton.setTextColor(AppCompatResources.getColorStateList(getContext(), R.color.conversation_update_item_button_text_color_wallpaper)); 674 } else { 675 actionButton.setBackgroundTintList(AppCompatResources.getColorStateList(getContext(), R.color.conversation_update_item_button_background_normal)); 676 actionButton.setTextColor(AppCompatResources.getColorStateList(getContext(), R.color.conversation_update_item_button_text_color_normal)); 677 } 678 } 679 680 private static boolean isSameType(@NonNull MessageRecord current, @NonNull MessageRecord candidate) { 681 return (current.isGroupUpdate() && candidate.isGroupUpdate()) || 682 (current.isProfileChange() && candidate.isProfileChange()) || 683 (current.isGroupCall() && candidate.isGroupCall()) || 684 (current.isExpirationTimerUpdate() && candidate.isExpirationTimerUpdate()) || 685 (current.isChangeNumber() && candidate.isChangeNumber()); 686 } 687 688 @Override 689 public void setOnClickListener(View.OnClickListener l) { 690 super.setOnClickListener(new InternalClickListener(l)); 691 } 692 693 private final class PresentOnChange implements Observer<Recipient> { 694 695 @Override 696 public void onChanged(Recipient recipient) { 697 if (recipient.getId() == conversationRecipient.getId() && (conversationRecipient == null || !conversationRecipient.hasSameContent(recipient))) { 698 conversationRecipient = recipient; 699 present(conversationMessage, nextMessageRecord, conversationRecipient, isMessageRequestAccepted); 700 } 701 } 702 } 703 704 private final class UpdateObserver implements Observer<Spannable> { 705 706 @Override 707 public void onChanged(Spannable update) { 708 setBodyText(update); 709 } 710 } 711 712 private class InternalClickListener implements View.OnClickListener { 713 714 @Nullable private final View.OnClickListener parent; 715 716 InternalClickListener(@Nullable View.OnClickListener parent) { 717 this.parent = parent; 718 } 719 720 @Override 721 public void onClick(View v) { 722 if ((!messageRecord.isIdentityUpdate() && 723 !messageRecord.isIdentityDefault() && 724 !messageRecord.isIdentityVerified()) || 725 !batchSelected.isEmpty()) 726 { 727 if (parent != null) parent.onClick(v); 728 return; 729 } 730 731 IdentityUtil.getRemoteIdentityKey(getContext(), messageRecord.getToRecipient()).addListener(new ListenableFuture.Listener<>() { 732 @Override 733 public void onSuccess(Optional<IdentityRecord> result) { 734 if (result.isPresent()) { 735 getContext().startActivity(VerifyIdentityActivity.newIntent(getContext(), result.get())); 736 } 737 } 738 739 @Override 740 public void onFailure(ExecutionException e) { 741 Log.w(TAG, e); 742 } 743 }); 744 } 745 } 746}