That fuck shit the fascists are using
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}