From 1dc29fda12a963eb4e8ff6d9d660c6f3cb1a89dd Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 10 Nov 2022 14:45:45 -0500 Subject: [PATCH] Add in-chat payment messages. --- .../conversation/ConversationDataSource.java | 56 +++++++++++- .../conversation/ConversationFragment.java | 12 +++ .../conversation/ConversationItem.java | 48 ++++++++-- .../ConversationReactionOverlay.java | 5 + .../conversation/ConversationUpdateItem.java | 2 +- .../securesms/conversation/MenuState.java | 23 ++++- .../quotes/MessageQuotesRepository.kt | 8 +- .../ui/payment/PaymentMessageView.kt | 63 +++++++++++++ .../ConversationListFragment.java | 7 +- .../securesms/database/MessageDatabase.java | 2 +- .../securesms/database/MmsDatabase.java | 37 ++++++-- .../securesms/database/MmsSmsColumns.java | 13 ++- .../securesms/database/PaymentDatabase.java | 45 ++++++++- .../securesms/database/SearchDatabase.java | 4 +- .../securesms/database/ThreadBodyUtil.java | 6 +- .../database/model/DisplayRecord.java | 11 ++- .../database/model/MediaMmsMessageRecord.java | 26 +++++- .../database/model/MessageRecord.java | 4 +- .../securesms/jobs/JobManagerFactories.java | 1 + .../jobs/PaymentNotificationSendJob.java | 11 ++- .../jobs/PaymentNotificationSendJobV2.kt | 91 +++++++++++++++++++ .../securesms/jobs/PaymentSendJob.java | 2 +- .../securesms/jobs/PushMediaSendJob.java | 44 ++++++--- .../jobs/SendPaymentsActivatedJob.kt | 6 ++ .../messagedetails/MessageRecordLiveData.java | 7 +- .../messages/MessageContentProcessor.java | 52 +++++++++-- .../securesms/mms/IncomingMediaMessage.kt | 27 ++++++ .../securesms/mms/OutgoingMediaMessage.java | 4 + ...Messages.kt => OutgoingPaymentMessages.kt} | 45 +++++++-- .../notifications/v2/NotificationItem.kt | 2 + .../securesms/payments/MoneyView.java | 8 +- .../preferences/PaymentsActivity.java | 16 ++++ .../securesms/sms/MessageSender.java | 2 +- .../securesms/util/FeatureFlags.java | 12 ++- .../securesms/util/MessageRecordUtil.kt | 11 +-- .../securesms/util/RemoteDeleteUtil.java | 1 + .../res/layout/conversation_item_payment.xml | 4 + .../conversation_item_received_multimedia.xml | 6 ++ .../conversation_item_sent_multimedia.xml | 6 ++ .../main/res/layout/payment_message_view.xml | 51 +++++++++++ app/src/main/res/values/strings.xml | 12 +++ .../MessageBitmaskColumnTransformer.kt | 6 +- .../securesms/database/FakeMessageRecords.kt | 7 +- 43 files changed, 708 insertions(+), 98 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/ui/payment/PaymentMessageView.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJobV2.kt rename app/src/main/java/org/thoughtcrime/securesms/mms/{OutgoingPaymentActivationMessages.kt => OutgoingPaymentMessages.kt} (58%) create mode 100644 app/src/main/res/layout/conversation_item_payment.xml create mode 100644 app/src/main/res/layout/payment_message_view.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java index 42dc29c5e..d3f48b5be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java @@ -7,12 +7,14 @@ import androidx.annotation.Nullable; import com.annimon.stream.Stream; +import org.signal.core.util.Stopwatch; import org.signal.core.util.logging.Log; import org.signal.paging.PagedDataSource; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.conversation.ConversationData.MessageRequestData; import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory; import org.thoughtcrime.securesms.database.MessageDatabase; +import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord; @@ -24,11 +26,12 @@ import org.thoughtcrime.securesms.database.model.ReactionRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.UpdateDescription; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.payments.Payment; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.Stopwatch; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.push.ServiceId; +import org.whispersystems.signalservice.api.util.UuidUtil; import java.util.ArrayList; import java.util.Collection; @@ -39,6 +42,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -96,6 +100,7 @@ public class ConversationDataSource implements PagedDataSource referencedIds = new HashSet<>(); try (MmsSmsDatabase.Reader reader = MmsSmsDatabase.readerFor(db.getConversation(threadId, start, length))) { @@ -105,6 +110,7 @@ public class ConversationDataSource implements PagedDataSource paymentMessages = new HashMap<>(); + private final Map messageIdToPayment = new HashMap<>(); + + public void add(MessageRecord messageRecord) { + if (messageRecord.isMms() && messageRecord.isPaymentNotification()) { + UUID paymentUuid = UuidUtil.parseOrNull(messageRecord.getBody()); + if (paymentUuid != null) { + paymentMessages.put(paymentUuid, messageRecord.getId()); + } + } + } + + public void fetchPayments() { + List payments = SignalDatabase.payments().getPayments(paymentMessages.keySet()); + for (Payment payment : payments) { + if (payment != null) { + messageIdToPayment.put(paymentMessages.get(payment.getUuid()), payment); + } + } + } + + @NonNull List buildUpdatedModels(@NonNull List records) { + return records.stream() + .map(record -> { + if (record instanceof MediaMmsMessageRecord) { + Payment payment = messageIdToPayment.get(record.getId()); + if (payment != null) { + return ((MediaMmsMessageRecord) record).withPayment(payment); + } + } + return record; + }) + .collect(Collectors.toList()); + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 0d9e818d6..dc8d2bfc2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -1168,6 +1168,15 @@ public class ConversationFragment extends LoggingFragment implements Multiselect }); } + private void handleViewPaymentDetails(MessageRecord message) { + if (message instanceof MediaMmsMessageRecord) { + MediaMmsMessageRecord mediaMessage = (MediaMmsMessageRecord) message; + if (mediaMessage.isPaymentNotification() && mediaMessage.getPayment() != null) { + startActivity(PaymentsActivity.navigateToPaymentDetails(requireContext(), mediaMessage.getPayment().getUuid())); + } + } + } + private void performSave(final MediaMmsMessageRecord message) { List attachments = Stream.of(message.getSlideDeck().getSlides()) .filter(s -> s.getUri() != null && (s.hasImage() || s.hasVideo() || s.hasAudio() || s.hasDocument())) @@ -2287,6 +2296,9 @@ public class ConversationFragment extends LoggingFragment implements Multiselect case COPY: handleCopyMessage(conversationMessage.getMultiselectCollection().toSet()); break; + case PAYMENT_DETAILS: + handleViewPaymentDetails(conversationMessage.getMessageRecord()); + break; case MULTISELECT: handleEnterMultiSelect(conversationMessage); break; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index f7f279941..3fd444c00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -95,6 +95,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors; import org.thoughtcrime.securesms.conversation.colors.Colorizer; import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection; import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart; +import org.thoughtcrime.securesms.conversation.ui.payment.PaymentMessageView; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MessageDatabase; @@ -181,14 +182,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private static final long MAX_CLUSTERING_TIME_DIFF = TimeUnit.MINUTES.toMillis(3); private static final int CONDENSED_MODE_MAX_LINES = 3; - private ConversationMessage conversationMessage; - private MessageRecord messageRecord; - private Optional nextMessageRecord; - private Locale locale; - private boolean groupThread; - private LiveRecipient recipient; - private GlideRequests glideRequests; - private ValueAnimator pulseOutlinerAlphaAnimator; + private ConversationMessage conversationMessage; + private MessageRecord messageRecord; + private Optional nextMessageRecord; + private Locale locale; + private boolean groupThread; + private LiveRecipient recipient; + private GlideRequests glideRequests; + private ValueAnimator pulseOutlinerAlphaAnimator; private Optional previousMessage; private ConversationItemDisplayMode displayMode; @@ -224,6 +225,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private Stub revealableStub; private Stub