Add in-chat payment activation requests.

Co-authored-by: Varsha <varsha@mobilecoin.com>
This commit is contained in:
Cody Henthorne
2022-11-01 11:50:41 -04:00
parent 8c915572fb
commit 77beeda62a
36 changed files with 595 additions and 51 deletions

View File

@@ -39,7 +39,10 @@ import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SimpleTask;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
@@ -52,27 +55,36 @@ import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.thoughtcrime.securesms.conversation.MessageSendType;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.maps.PlacePickerActivity;
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory;
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Fragment;
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity;
import org.thoughtcrime.securesms.payments.CanNotSendPaymentDialog;
import org.thoughtcrime.securesms.payments.MobileCoinPublicAddress;
import org.thoughtcrime.securesms.payments.PaymentsAddressException;
import org.thoughtcrime.securesms.payments.create.CreatePaymentFragmentArgs;
import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity;
import org.thoughtcrime.securesms.payments.preferences.RecipientHasNotEnabledPaymentsDialog;
import org.thoughtcrime.securesms.payments.preferences.model.PayeeParcelable;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.providers.DeprecatedPersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.signalservice.api.util.ExpiringProfileCredentialUtil;
import java.io.IOException;
import java.util.Collections;
@@ -81,6 +93,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class AttachmentManager {
@@ -419,11 +432,45 @@ public class AttachmentManager {
fragment.startActivityForResult(intent, requestCode);
}
public static void selectPayment(@NonNull Fragment fragment, @NonNull RecipientId recipientId) {
Intent intent = new Intent(fragment.requireContext(), PaymentsActivity.class);
intent.putExtra(PaymentsActivity.EXTRA_PAYMENTS_STARTING_ACTION, R.id.action_directly_to_createPayment);
intent.putExtra(PaymentsActivity.EXTRA_STARTING_ARGUMENTS, new CreatePaymentFragmentArgs.Builder(new PayeeParcelable(recipientId)).build().toBundle());
fragment.startActivity(intent);
public static void selectPayment(@NonNull Fragment fragment, @NonNull Recipient recipient) {
if (!ExpiringProfileCredentialUtil.isValid(recipient.getExpiringProfileKeyCredential())) {
CanNotSendPaymentDialog.show(fragment.requireContext());
return;
}
SimpleTask.run(fragment.getViewLifecycleOwner().getLifecycle(),
() -> {
try {
return ProfileUtil.getAddressForRecipient(recipient);
} catch (IOException | PaymentsAddressException e) {
Log.w(TAG, "Could not get address for recipient: ", e);
return null;
}
},
(address) -> {
if (address != null) {
Intent intent = new Intent(fragment.requireContext(), PaymentsActivity.class);
intent.putExtra(PaymentsActivity.EXTRA_PAYMENTS_STARTING_ACTION, R.id.action_directly_to_createPayment);
intent.putExtra(PaymentsActivity.EXTRA_STARTING_ARGUMENTS, new CreatePaymentFragmentArgs.Builder(new PayeeParcelable(recipient.getId())).build().toBundle());
fragment.startActivity(intent);
} else if (FeatureFlags.paymentsRequestActivateFlow()) {
showRequestToActivatePayments(fragment.requireContext(), recipient);
} else {
RecipientHasNotEnabledPaymentsDialog.show(fragment.requireContext());
}
});
}
public static void showRequestToActivatePayments(@NonNull Context context, @NonNull Recipient recipient) {
new MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.AttachmentManager__not_activated_payments, recipient.getShortDisplayName(context)))
.setMessage(context.getString(R.string.AttachmentManager__request_to_activate_payments))
.setPositiveButton(context.getString(R.string.AttachmentManager__send_request), (dialog, which) -> {
OutgoingRequestToActivatePaymentMessages outgoingMessage = new OutgoingRequestToActivatePaymentMessages(recipient, System.currentTimeMillis(), 0);
MessageSender.send(context, outgoingMessage, SignalDatabase.threads().getOrCreateThreadIdFor(recipient), false, null, null);
})
.setNegativeButton(context.getString(R.string.AttachmentManager__cancel), null)
.show();
}
private @Nullable Uri getSlideUri() {

View File

@@ -38,7 +38,9 @@ class IncomingMediaMessage(
sharedContacts: List<Contact> = emptyList(),
linkPreviews: List<LinkPreview> = emptyList(),
mentions: List<Mention> = emptyList(),
val giftBadge: GiftBadge? = null
val giftBadge: GiftBadge? = null,
val isActivatePaymentsRequest: Boolean = false,
val isPaymentsActivated: Boolean = false
) {
val attachments: List<Attachment> = ArrayList(attachments)
@@ -61,7 +63,9 @@ class IncomingMediaMessage(
expirationUpdate: Boolean,
viewOnce: Boolean,
unidentified: Boolean,
sharedContacts: Optional<List<Contact>>
sharedContacts: Optional<List<Contact>>,
activatePaymentsRequest: Boolean,
paymentsActivated: Boolean
) : this(
from = from,
groupId = groupId.orElse(null),
@@ -79,6 +83,8 @@ class IncomingMediaMessage(
serverGuid = null,
attachments = attachments?.let { ArrayList<Attachment>(it) } ?: emptyList(),
sharedContacts = ArrayList<Contact>(sharedContacts.orElse(emptyList())),
isActivatePaymentsRequest = activatePaymentsRequest,
isPaymentsActivated = paymentsActivated
)
constructor(
@@ -103,7 +109,9 @@ class IncomingMediaMessage(
mentions: Optional<List<Mention>>,
sticker: Optional<Attachment>,
serverGuid: String?,
giftBadge: GiftBadge?
giftBadge: GiftBadge?,
activatePaymentsRequest: Boolean,
paymentsActivated: Boolean
) : this(
from = from,
groupId = if (group.isPresent) GroupId.v2(group.get().masterKey) else null,
@@ -126,6 +134,8 @@ class IncomingMediaMessage(
sharedContacts = sharedContacts.orElse(emptyList()),
linkPreviews = linkPreviews.orElse(emptyList()),
mentions = mentions.orElse(emptyList()),
giftBadge = giftBadge
giftBadge = giftBadge,
isActivatePaymentsRequest = activatePaymentsRequest,
isPaymentsActivated = paymentsActivated
)
}

View File

@@ -193,6 +193,14 @@ public class OutgoingMediaMessage {
return false;
}
public boolean isRequestToActivatePayments() {
return false;
}
public boolean isPaymentsActivated() {
return false;
}
public long getSentTimeMillis() {
return sentTimeMillis;
}

View File

@@ -0,0 +1,59 @@
package org.thoughtcrime.securesms.mms
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.recipients.Recipient
import java.util.LinkedList
/**
* Specialized message sent to request someone activate payments.
*/
class OutgoingRequestToActivatePaymentMessages(
recipient: Recipient,
sentTimeMillis: Long,
expiresIn: Long
) : OutgoingSecureMediaMessage(
recipient,
"",
LinkedList(),
sentTimeMillis,
ThreadDatabase.DistributionTypes.CONVERSATION,
expiresIn,
false,
StoryType.NONE,
null,
false,
null, emptyList(), emptyList(), emptyList(),
null
) {
override fun isRequestToActivatePayments(): Boolean {
return true
}
}
/**
* Specialized message sent to indicate you activated payments. Intended to only
* be sent to those that sent requests prior to activation.
*/
class OutgoingPaymentsActivatedMessages(
recipient: Recipient,
sentTimeMillis: Long,
expiresIn: Long
) : OutgoingSecureMediaMessage(
recipient,
"",
LinkedList(),
sentTimeMillis,
ThreadDatabase.DistributionTypes.CONVERSATION,
expiresIn,
false,
StoryType.NONE,
null,
false,
null, emptyList(), emptyList(), emptyList(),
null
) {
override fun isPaymentsActivated(): Boolean {
return true
}
}