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

@@ -12,6 +12,7 @@ import com.google.android.mms.pdu_alt.NotificationInd;
import net.zetetic.database.sqlcipher.SQLiteStatement;
import org.signal.core.util.CursorExtensionsKt;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SQLiteDatabaseExtensionsKt;
import org.signal.core.util.SqlUtil;
@@ -517,6 +518,15 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns,
return data;
}
public List<Long> getIncomingPaymentRequestThreads() {
Cursor cursor = SQLiteDatabaseExtensionsKt.select(getReadableDatabase(), "DISTINCT " + THREAD_ID)
.from(getTableName())
.where("(" + getTypeField() + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_INBOX_TYPE + " AND (" + getTypeField() + " & ?) != 0", Types.SPECIAL_TYPE_ACTIVATE_PAYMENTS_REQUEST)
.run();
return CursorExtensionsKt.readToList(cursor, c -> CursorUtil.requireLong(c, THREAD_ID));
}
@Override
public void remapRecipient(@NonNull RecipientId fromId, @NonNull RecipientId toId) {
ContentValues values = new ContentValues();

View File

@@ -78,6 +78,8 @@ import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingPaymentsActivatedMessages;
import org.thoughtcrime.securesms.mms.OutgoingRequestToActivatePaymentMessages;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck;
@@ -1794,6 +1796,10 @@ public class MmsDatabase extends MessageDatabase {
return new OutgoingGroupUpdateMessage(recipient, new MessageGroupContext(body, Types.isGroupV2(outboxType)), attachments, timestamp, 0, false, quote, contacts, previews, mentions);
} else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
} else if (Types.isRequestToActivatePayments(outboxType)) {
return new OutgoingRequestToActivatePaymentMessages(recipient, timestamp, expiresIn);
} else if (Types.isPaymentsActivated(outboxType)) {
return new OutgoingPaymentsActivatedMessages(recipient, timestamp, expiresIn);
}
GiftBadge giftBadge = null;
@@ -1987,7 +1993,7 @@ public class MmsDatabase extends MessageDatabase {
updateThread);
boolean isNotStoryGroupReply = retrieved.getParentStoryId() == null || !retrieved.getParentStoryId().isGroupReply();
if (!Types.isExpirationTimerUpdate(mailbox) && !retrieved.getStoryType().isStory() && isNotStoryGroupReply) {
if (!Types.isPaymentsActivated(mailbox) && !Types.isRequestToActivatePayments(mailbox) && !Types.isExpirationTimerUpdate(mailbox) && !retrieved.getStoryType().isStory() && isNotStoryGroupReply) {
boolean incrementUnreadMentions = !retrieved.getMentions().isEmpty() && retrieved.getMentions().stream().anyMatch(m -> m.getRecipientId().equals(Recipient.self().getId()));
SignalDatabase.threads().incrementUnread(threadId, 1, incrementUnreadMentions ? 1 : 0);
SignalDatabase.threads().update(threadId, true);
@@ -2013,6 +2019,14 @@ public class MmsDatabase extends MessageDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
}
if (retrieved.isActivatePaymentsRequest()) {
type |= Types.SPECIAL_TYPE_ACTIVATE_PAYMENTS_REQUEST;
}
if (retrieved.isPaymentsActivated()) {
type |= Types.SPECIAL_TYPE_PAYMENTS_ACTIVATED;
}
return insertMessageInbox(retrieved, contentLocation, threadId, type);
}
@@ -2044,6 +2058,20 @@ public class MmsDatabase extends MessageDatabase {
type |= Types.SPECIAL_TYPE_GIFT_BADGE;
}
if (retrieved.isActivatePaymentsRequest()) {
if (hasSpecialType) {
throw new MmsException("Cannot insert message with multiple special types.");
}
type |= Types.SPECIAL_TYPE_ACTIVATE_PAYMENTS_REQUEST;
}
if (retrieved.isPaymentsActivated()) {
if (hasSpecialType) {
throw new MmsException("Cannot insert message with multiple special types.");
}
type |= Types.SPECIAL_TYPE_PAYMENTS_ACTIVATED;
}
return insertMessageInbox(retrieved, "", threadId, type);
}
@@ -2211,6 +2239,20 @@ public class MmsDatabase extends MessageDatabase {
type |= Types.SPECIAL_TYPE_GIFT_BADGE;
}
if (message.isRequestToActivatePayments()) {
if (hasSpecialType) {
throw new MmsException("Cannot insert message with multiple special types.");
}
type |= Types.SPECIAL_TYPE_ACTIVATE_PAYMENTS_REQUEST;
}
if (message.isPaymentsActivated()) {
if (hasSpecialType) {
throw new MmsException("Cannot insert message with multiple special types.");
}
type |= Types.SPECIAL_TYPE_PAYMENTS_ACTIVATED;
}
Map<RecipientId, EarlyReceiptCache.Receipt> earlyDeliveryReceipts = earlyDeliveryReceiptCache.remove(message.getSentTimeMillis());
ContentValues contentValues = new ContentValues();

View File

@@ -140,9 +140,11 @@ public interface MmsSmsColumns {
protected static final long ENCRYPTION_REMOTE_LEGACY_BIT = 0x02000000;
// Special message types
public static final long SPECIAL_TYPES_MASK = 0xF00000000L;
public static final long SPECIAL_TYPE_STORY_REACTION = 0x100000000L;
public static final long SPECIAL_TYPE_GIFT_BADGE = 0x200000000L;
public static final long SPECIAL_TYPES_MASK = 0xF00000000L;
public static final long SPECIAL_TYPE_STORY_REACTION = 0x100000000L;
public static final long SPECIAL_TYPE_GIFT_BADGE = 0x200000000L;
protected static final long SPECIAL_TYPE_ACTIVATE_PAYMENTS_REQUEST = 0x400000000L;
protected static final long SPECIAL_TYPE_PAYMENTS_ACTIVATED = 0x800000000L;
public static boolean isStoryReaction(long type) {
return (type & SPECIAL_TYPES_MASK) == SPECIAL_TYPE_STORY_REACTION;
@@ -152,6 +154,14 @@ public interface MmsSmsColumns {
return (type & SPECIAL_TYPES_MASK) == SPECIAL_TYPE_GIFT_BADGE;
}
public static boolean isRequestToActivatePayments(long type) {
return (type & SPECIAL_TYPES_MASK) == SPECIAL_TYPE_ACTIVATE_PAYMENTS_REQUEST;
}
public static boolean isPaymentsActivated(long type) {
return (type & SPECIAL_TYPES_MASK) == SPECIAL_TYPE_PAYMENTS_ACTIVATED;
}
public static boolean isDraftMessageType(long type) {
return (type & BASE_TYPE_MASK) == BASE_DRAFT_TYPE;
}

View File

@@ -39,7 +39,6 @@ import org.signal.core.util.SQLiteDatabaseExtensionsKt;
import org.signal.core.util.SqlUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.util.Pair;
import org.thoughtcrime.securesms.components.settings.app.chats.sms.SmsExportState;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;

View File

@@ -52,6 +52,10 @@ public final class ThreadBodyUtil {
return String.format("%s %s", EmojiStrings.GIFT, getGiftSummary(context, record));
} else if (MessageRecordUtil.isStoryReaction(record)) {
return getStoryReactionSummary(context, record);
} else if (MessageRecordUtil.isPaymentActivationRequest(record)) {
return String.format("%s %s", EmojiStrings.CARD, getPaymentActivationRequestSummary(context, record));
} else if (MessageRecordUtil.isPaymentsActivated(record)) {
return String.format("%s %s", EmojiStrings.CARD, getPaymentActivatedSummary(context, record));
}
boolean hasImage = false;
@@ -94,6 +98,22 @@ public final class ThreadBodyUtil {
return context.getString(R.string.ThreadRecord__reacted_s_to_your_story, messageRecord.getDisplayBody(context));
}
}
private static @NonNull String getPaymentActivationRequestSummary(@NonNull Context context, @NonNull MessageRecord messageRecord) {
if (messageRecord.isOutgoing()) {
return context.getString(R.string.ThreadRecord_you_sent_request);
} else {
return context.getString(R.string.ThreadRecord_wants_you_to_activate_payments, messageRecord.getRecipient().getShortDisplayName(context));
}
}
private static @NonNull String getPaymentActivatedSummary(@NonNull Context context, @NonNull MessageRecord messageRecord) {
if (messageRecord.isOutgoing()) {
return context.getString(R.string.ThreadRecord_you_activated_payments);
} else {
return context.getString(R.string.ThreadRecord_can_accept_payments, messageRecord.getRecipient().getShortDisplayName(context));
}
}
private static @NonNull String format(@NonNull Context context, @NonNull MessageRecord record, @NonNull String emoji, @StringRes int defaultStringRes) {
return String.format("%s %s", emoji, getBodyOrDefault(context, record, defaultStringRes));

View File

@@ -22,6 +22,7 @@ import android.text.SpannableString;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -231,4 +232,12 @@ public abstract class DisplayRecord {
public boolean isPendingInsecureSmsFallback() {
return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type);
}
public boolean isRequestToActivatePayments() {
return SmsDatabase.Types.isRequestToActivatePayments(type);
}
public boolean isPaymentsActivated() {
return SmsDatabase.Types.isPaymentsActivated(type);
}
}

View File

@@ -240,6 +240,12 @@ public abstract class MessageRecord extends DisplayRecord {
int messageResource = SignalStore.misc().getSmsExportPhase().isSmsSupported() ? R.string.MessageRecord__you_will_no_longer_be_able_to_send_sms_messages_from_signal_soon
: R.string.MessageRecord__you_can_no_longer_send_sms_messages_in_signal;
return fromRecipient(getIndividualRecipient(), r -> context.getString(messageResource, r.getDisplayName(context)), R.drawable.ic_update_info_16);
} else if (isRequestToActivatePayments()) {
return isOutgoing() ? fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_sent_request, r.getShortDisplayName(context)), R.drawable.ic_card_activate_payments)
: fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_wants_you_to_activate_payments, r.getShortDisplayName(context)), R.drawable.ic_card_activate_payments);
} else if (isPaymentsActivated()) {
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_activated_payments), R.drawable.ic_card_activate_payments)
: fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_can_accept_payments, r.getShortDisplayName(context)), R.drawable.ic_card_activate_payments);
}
return null;
@@ -570,7 +576,8 @@ public abstract class MessageRecord extends DisplayRecord {
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType();
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType() ||
isRequestToActivatePayments() || isPaymentsActivated();
}
public boolean isMediaPending() {