Search in sources :

Example 1 with MultiselectPart

use of org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart in project Signal-Android by WhisperSystems.

the class ConversationUpdateItem method present.

private void present(@NonNull ConversationMessage conversationMessage, @NonNull Optional<MessageRecord> nextMessageRecord, @NonNull Recipient conversationRecipient, boolean isMessageRequestAccepted) {
    Set<MultiselectPart> multiselectParts = conversationMessage.getMultiselectCollection().toSet();
    setSelected(!Sets.intersection(multiselectParts, batchSelected).isEmpty());
    if (conversationMessage.getMessageRecord().isGroupV1MigrationEvent() && (!nextMessageRecord.isPresent() || !nextMessageRecord.get().isGroupV1MigrationEvent())) {
        actionButton.setText(R.string.ConversationUpdateItem_learn_more);
        actionButton.setVisibility(VISIBLE);
        actionButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onGroupMigrationLearnMoreClicked(conversationMessage.getMessageRecord().getGroupV1MigrationMembershipChanges());
            }
        });
    } else if (conversationMessage.getMessageRecord().isChatSessionRefresh() && (!nextMessageRecord.isPresent() || !nextMessageRecord.get().isChatSessionRefresh())) {
        actionButton.setText(R.string.ConversationUpdateItem_learn_more);
        actionButton.setVisibility(VISIBLE);
        actionButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onChatSessionRefreshLearnMoreClicked();
            }
        });
    } else if (conversationMessage.getMessageRecord().isIdentityUpdate()) {
        actionButton.setText(R.string.ConversationUpdateItem_learn_more);
        actionButton.setVisibility(VISIBLE);
        actionButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onSafetyNumberLearnMoreClicked(conversationMessage.getMessageRecord().getIndividualRecipient());
            }
        });
    } else if (conversationMessage.getMessageRecord().isGroupCall()) {
        UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
        Collection<ServiceId> acis = updateDescription.getMentioned();
        int text = 0;
        if (Util.hasItems(acis)) {
            if (acis.contains(Recipient.self().requireServiceId())) {
                text = R.string.ConversationUpdateItem_return_to_call;
            } else if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).getIsCallFull()) {
                text = R.string.ConversationUpdateItem_call_is_full;
            } else {
                text = R.string.ConversationUpdateItem_join_call;
            }
        }
        if (text != 0 && conversationRecipient.isGroup() && conversationRecipient.isActiveGroup()) {
            actionButton.setText(text);
            actionButton.setVisibility(VISIBLE);
            actionButton.setOnClickListener(v -> {
                if (batchSelected.isEmpty() && eventListener != null) {
                    eventListener.onJoinGroupCallClicked();
                }
            });
        } else {
            actionButton.setVisibility(GONE);
            actionButton.setOnClickListener(null);
        }
    } else if (conversationMessage.getMessageRecord().isSelfCreatedGroup()) {
        actionButton.setText(R.string.ConversationUpdateItem_invite_friends);
        actionButton.setVisibility(VISIBLE);
        actionButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onInviteFriendsToGroupClicked(conversationRecipient.requireGroupId().requireV2());
            }
        });
    } else if ((conversationMessage.getMessageRecord().isMissedAudioCall() || conversationMessage.getMessageRecord().isMissedVideoCall()) && EnableCallNotificationSettingsDialog.shouldShow(getContext())) {
        actionButton.setVisibility(VISIBLE);
        actionButton.setText(R.string.ConversationUpdateItem_enable_call_notifications);
        actionButton.setOnClickListener(v -> {
            if (eventListener != null) {
                eventListener.onEnableCallNotificationsClicked();
            }
        });
    } else if (conversationMessage.getMessageRecord().isInMemoryMessageRecord() && ((InMemoryMessageRecord) conversationMessage.getMessageRecord()).showActionButton()) {
        InMemoryMessageRecord inMemoryMessageRecord = (InMemoryMessageRecord) conversationMessage.getMessageRecord();
        actionButton.setVisibility(VISIBLE);
        actionButton.setText(inMemoryMessageRecord.getActionButtonText());
        actionButton.setOnClickListener(v -> {
            if (eventListener != null) {
                eventListener.onInMemoryMessageClicked(inMemoryMessageRecord);
            }
        });
    } else if (conversationMessage.getMessageRecord().isGroupV2DescriptionUpdate()) {
        actionButton.setVisibility(VISIBLE);
        actionButton.setText(R.string.ConversationUpdateItem_view);
        actionButton.setOnClickListener(v -> {
            if (eventListener != null) {
                eventListener.onViewGroupDescriptionChange(conversationRecipient.getGroupId().orNull(), conversationMessage.getMessageRecord().getGroupV2DescriptionUpdate(), isMessageRequestAccepted);
            }
        });
    } else if (conversationMessage.getMessageRecord().isBadDecryptType() && (!nextMessageRecord.isPresent() || !nextMessageRecord.get().isBadDecryptType())) {
        actionButton.setText(R.string.ConversationUpdateItem_learn_more);
        actionButton.setVisibility(VISIBLE);
        actionButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onBadDecryptLearnMoreClicked(conversationMessage.getMessageRecord().getRecipient().getId());
            }
        });
    } else if (conversationMessage.getMessageRecord().isChangeNumber() && conversationMessage.getMessageRecord().getIndividualRecipient().isSystemContact()) {
        actionButton.setText(R.string.ConversationUpdateItem_update_contact);
        actionButton.setVisibility(VISIBLE);
        actionButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onChangeNumberUpdateContact(conversationMessage.getMessageRecord().getIndividualRecipient());
            }
        });
    } else {
        actionButton.setVisibility(GONE);
        actionButton.setOnClickListener(null);
    }
    if (conversationMessage.getMessageRecord().isBoostRequest()) {
        actionButton.setVisibility(GONE);
        CardView donateButton = donateButtonStub.get();
        TextView buttonText = donateButton.findViewById(R.id.conversation_update_donate_action_button);
        boolean isSustainer = SignalStore.donationsValues().isLikelyASustainer();
        donateButton.setVisibility(VISIBLE);
        donateButton.setOnClickListener(v -> {
            if (batchSelected.isEmpty() && eventListener != null) {
                eventListener.onDonateClicked();
            }
        });
        if (isSustainer) {
            buttonText.setText(R.string.ConversationUpdateItem_signal_boost);
            buttonText.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_boost_outline_16, 0, 0, 0);
        } else {
            buttonText.setText(R.string.ConversationUpdateItem_become_a_sustainer);
            buttonText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0);
        }
        AutoRounder.autoSetCorners(donateButton, donateButton::setRadius);
    } else if (donateButtonStub.resolved()) {
        donateButtonStub.get().setVisibility(GONE);
    }
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) NonNull(androidx.annotation.NonNull) Spannable(android.text.Spannable) FrameLayout(android.widget.FrameLayout) ViewUtil(org.thoughtcrime.securesms.util.ViewUtil) R(org.thoughtcrime.securesms.R) MessageRecord(org.thoughtcrime.securesms.database.model.MessageRecord) ColorStateList(android.content.res.ColorStateList) AttributeSet(android.util.AttributeSet) Locale(java.util.Locale) View(android.view.View) Recipient(org.thoughtcrime.securesms.recipients.Recipient) BindableConversationItem(org.thoughtcrime.securesms.BindableConversationItem) LiveUpdateMessage(org.thoughtcrime.securesms.database.model.LiveUpdateMessage) ContextCompat(androidx.core.content.ContextCompat) CardView(androidx.cardview.widget.CardView) ACI(org.whispersystems.signalservice.api.push.ACI) Collection(java.util.Collection) Set(java.util.Set) ViewGroup(android.view.ViewGroup) Sets(com.google.common.collect.Sets) Objects(java.util.Objects) Log(org.signal.core.util.logging.Log) TextView(android.widget.TextView) Nullable(androidx.annotation.Nullable) IdentityRecord(org.thoughtcrime.securesms.database.model.IdentityRecord) DateUtils(org.thoughtcrime.securesms.util.DateUtils) GlideRequests(org.thoughtcrime.securesms.mms.GlideRequests) Projection(org.thoughtcrime.securesms.util.Projection) Context(android.content.Context) UpdateDescription(org.thoughtcrime.securesms.database.model.UpdateDescription) MultiselectPart(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart) Util(org.thoughtcrime.securesms.util.Util) AutoRounder(org.thoughtcrime.securesms.util.views.AutoRounder) LifecycleOwner(androidx.lifecycle.LifecycleOwner) MaterialButton(com.google.android.material.button.MaterialButton) Colorizer(org.thoughtcrime.securesms.conversation.colors.Colorizer) VerifyIdentityActivity(org.thoughtcrime.securesms.verify.VerifyIdentityActivity) LiveData(androidx.lifecycle.LiveData) IdentityUtil(org.thoughtcrime.securesms.util.IdentityUtil) ListenableFuture(org.thoughtcrime.securesms.util.concurrent.ListenableFuture) LiveRecipient(org.thoughtcrime.securesms.recipients.LiveRecipient) Stub(org.thoughtcrime.securesms.util.views.Stub) SpannableString(android.text.SpannableString) InMemoryMessageRecord(org.thoughtcrime.securesms.database.model.InMemoryMessageRecord) Optional(org.whispersystems.libsignal.util.guava.Optional) ExecutionException(java.util.concurrent.ExecutionException) Observer(androidx.lifecycle.Observer) GroupCallUpdateDetailsUtil(org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil) ThemeUtil(org.thoughtcrime.securesms.util.ThemeUtil) EnableCallNotificationSettingsDialog(org.thoughtcrime.securesms.conversation.ui.error.EnableCallNotificationSettingsDialog) ServiceId(org.whispersystems.signalservice.api.push.ServiceId) ProjectionList(org.thoughtcrime.securesms.util.ProjectionList) LiveDataUtil(org.thoughtcrime.securesms.util.livedata.LiveDataUtil) MultiselectPart(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart) InMemoryMessageRecord(org.thoughtcrime.securesms.database.model.InMemoryMessageRecord) UpdateDescription(org.thoughtcrime.securesms.database.model.UpdateDescription) CardView(androidx.cardview.widget.CardView) Collection(java.util.Collection) TextView(android.widget.TextView)

Example 2 with MultiselectPart

use of org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart in project Signal-Android by WhisperSystems.

the class ConversationItem method getMultiselectPartForLatestTouch.

@Override
@NonNull
public MultiselectPart getMultiselectPartForLatestTouch() {
    MultiselectCollection parts = conversationMessage.getMultiselectCollection();
    if (parts.isSingle()) {
        return parts.asSingle().getSinglePart();
    }
    MultiselectPart top = parts.asDouble().getTopPart();
    MultiselectPart bottom = parts.asDouble().getBottomPart();
    if (hasThumbnail(messageRecord)) {
        return isTouchBelowBoundary(mediaThumbnailStub.require()) ? bottom : top;
    } else if (hasDocument(messageRecord)) {
        return isTouchBelowBoundary(documentViewStub.get()) ? bottom : top;
    } else if (hasAudio(messageRecord)) {
        return isTouchBelowBoundary(audioViewStub.get()) ? bottom : top;
    }
    {
        throw new IllegalStateException("Found a situation where we have something other than a thumbnail or a document.");
    }
}
Also used : MultiselectPart(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart) MultiselectCollection(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection) NonNull(androidx.annotation.NonNull)

Example 3 with MultiselectPart

use of org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart in project Signal-Android by WhisperSystems.

the class MenuState method getMenuState.

static MenuState getMenuState(@NonNull Recipient conversationRecipient, @NonNull Set<MultiselectPart> selectedParts, boolean shouldShowMessageRequest, boolean isNonAdminInAnnouncementGroup) {
    Builder builder = new Builder();
    boolean actionMessage = false;
    boolean hasText = false;
    boolean sharedContact = false;
    boolean viewOnce = false;
    boolean remoteDelete = false;
    boolean hasInMemory = false;
    boolean hasPendingMedia = false;
    boolean mediaIsSelected = false;
    for (MultiselectPart part : selectedParts) {
        MessageRecord messageRecord = part.getMessageRecord();
        if (isActionMessage(messageRecord)) {
            actionMessage = true;
            if (messageRecord.isInMemoryMessageRecord()) {
                hasInMemory = true;
            }
        }
        if (!(part instanceof MultiselectPart.Attachments)) {
            if (messageRecord.getBody().length() > 0) {
                hasText = true;
            }
        } else {
            mediaIsSelected = true;
            if (messageRecord.isMediaPending()) {
                hasPendingMedia = true;
            }
        }
        if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) {
            sharedContact = true;
        }
        if (messageRecord.isViewOnce()) {
            viewOnce = true;
        }
        if (messageRecord.isRemoteDelete()) {
            remoteDelete = true;
        }
    }
    boolean shouldShowForwardAction = !actionMessage && !sharedContact && !viewOnce && !remoteDelete && !hasPendingMedia && selectedParts.size() <= MAX_FORWARDABLE_COUNT;
    int uniqueRecords = selectedParts.stream().map(MultiselectPart::getMessageRecord).collect(Collectors.toSet()).size();
    if (uniqueRecords > 1) {
        builder.shouldShowForwardAction(shouldShowForwardAction).shouldShowReplyAction(false).shouldShowDetailsAction(false).shouldShowSaveAttachmentAction(false).shouldShowResendAction(false);
    } else {
        MessageRecord messageRecord = selectedParts.iterator().next().getMessageRecord();
        builder.shouldShowResendAction(messageRecord.isFailed()).shouldShowSaveAttachmentAction(mediaIsSelected && !actionMessage && !viewOnce && messageRecord.isMms() && !hasPendingMedia && !messageRecord.isMmsNotification() && ((MediaMmsMessageRecord) messageRecord).containsMediaSlide() && ((MediaMmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide() == null).shouldShowForwardAction(shouldShowForwardAction).shouldShowDetailsAction(!actionMessage && !conversationRecipient.isReleaseNotes()).shouldShowReplyAction(canReplyToMessage(conversationRecipient, actionMessage, messageRecord, shouldShowMessageRequest, isNonAdminInAnnouncementGroup));
    }
    return builder.shouldShowCopyAction(!actionMessage && !remoteDelete && hasText).shouldShowDeleteAction(!hasInMemory && onlyContainsCompleteMessages(selectedParts)).shouldShowReactions(!conversationRecipient.isReleaseNotes()).build();
}
Also used : MultiselectPart(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart) MediaMmsMessageRecord(org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord) MmsMessageRecord(org.thoughtcrime.securesms.database.model.MmsMessageRecord) MediaMmsMessageRecord(org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord) MessageRecord(org.thoughtcrime.securesms.database.model.MessageRecord)

Example 4 with MultiselectPart

use of org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart in project Signal-Android by WhisperSystems.

the class ConversationFragment method setCorrectActionModeMenuVisibility.

private void setCorrectActionModeMenuVisibility() {
    Set<MultiselectPart> selectedParts = getListAdapter().getSelectedItems();
    if (actionMode != null && selectedParts.size() == 0) {
        actionMode.finish();
        return;
    }
    setBottomActionBarVisibility(true);
    MenuState menuState = MenuState.getMenuState(recipient.get(), selectedParts, messageRequestViewModel.shouldShowMessageRequest(), groupViewModel.isNonAdminInAnnouncementGroup());
    List<ActionItem> items = new ArrayList<>();
    if (menuState.shouldShowReplyAction()) {
        items.add(new ActionItem(R.drawable.ic_reply_24_tinted, getResources().getString(R.string.conversation_selection__menu_reply), () -> {
            maybeShowSwipeToReplyTooltip();
            handleReplyMessage(getSelectedConversationMessage());
            actionMode.finish();
        }));
    }
    if (menuState.shouldShowForwardAction()) {
        items.add(new ActionItem(R.drawable.ic_forward_24_tinted, getResources().getString(R.string.conversation_selection__menu_forward), () -> handleForwardMessageParts(selectedParts)));
    }
    if (menuState.shouldShowSaveAttachmentAction()) {
        items.add(new ActionItem(R.drawable.ic_save_24_tinted, getResources().getString(R.string.conversation_selection__menu_save), () -> {
            handleSaveAttachment((MediaMmsMessageRecord) getSelectedConversationMessage().getMessageRecord());
            actionMode.finish();
        }));
    }
    if (menuState.shouldShowCopyAction()) {
        items.add(new ActionItem(R.drawable.ic_copy_24_tinted, getResources().getString(R.string.conversation_selection__menu_copy), () -> {
            handleCopyMessage(selectedParts);
            actionMode.finish();
        }));
    }
    if (menuState.shouldShowDetailsAction()) {
        items.add(new ActionItem(R.drawable.ic_info_tinted_24, getResources().getString(R.string.conversation_selection__menu_message_details), () -> {
            handleDisplayDetails(getSelectedConversationMessage());
            actionMode.finish();
        }));
    }
    if (menuState.shouldShowDeleteAction()) {
        items.add(new ActionItem(R.drawable.ic_delete_tinted_24, getResources().getString(R.string.conversation_selection__menu_delete), () -> {
            handleDeleteMessages(selectedParts);
            actionMode.finish();
        }));
    }
    bottomActionBar.setItems(items);
}
Also used : MultiselectPart(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart) MediaMmsMessageRecord(org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord) ActionItem(org.thoughtcrime.securesms.components.menu.ActionItem) ArrayList(java.util.ArrayList)

Example 5 with MultiselectPart

use of org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart in project Signal-Android by WhisperSystems.

the class ConversationFragment method calculateSelectedItemCount.

@NonNull
private String calculateSelectedItemCount() {
    ConversationAdapter adapter = getListAdapter();
    int count = 0;
    if (adapter != null && !adapter.getSelectedItems().isEmpty()) {
        count = (int) adapter.getSelectedItems().stream().map(MultiselectPart::getConversationMessage).distinct().count();
    }
    return requireContext().getResources().getQuantityString(R.plurals.conversation_context__s_selected, count, count);
}
Also used : MultiselectPart(org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart) SuppressLint(android.annotation.SuppressLint) NonNull(androidx.annotation.NonNull)

Aggregations

MultiselectPart (org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart)5 NonNull (androidx.annotation.NonNull)3 MediaMmsMessageRecord (org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord)2 MessageRecord (org.thoughtcrime.securesms.database.model.MessageRecord)2 SuppressLint (android.annotation.SuppressLint)1 Context (android.content.Context)1 ColorStateList (android.content.res.ColorStateList)1 Spannable (android.text.Spannable)1 SpannableString (android.text.SpannableString)1 AttributeSet (android.util.AttributeSet)1 View (android.view.View)1 ViewGroup (android.view.ViewGroup)1 FrameLayout (android.widget.FrameLayout)1 TextView (android.widget.TextView)1 Nullable (androidx.annotation.Nullable)1 CardView (androidx.cardview.widget.CardView)1 ContextCompat (androidx.core.content.ContextCompat)1 LifecycleOwner (androidx.lifecycle.LifecycleOwner)1 LiveData (androidx.lifecycle.LiveData)1 Observer (androidx.lifecycle.Observer)1