diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/payment/PaymentMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/payment/PaymentMessageView.kt index d8ea2bd8a4..a39cfd43fb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/payment/PaymentMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/payment/PaymentMessageView.kt @@ -6,7 +6,11 @@ import android.text.style.TypefaceSpan import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout +import androidx.annotation.ColorInt import androidx.core.view.ViewCompat +import com.google.android.material.progressindicator.CircularProgressIndicatorSpec +import com.google.android.material.progressindicator.IndeterminateDrawable +import org.signal.core.util.dp import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.quotes.QuoteViewColorTheme import org.thoughtcrime.securesms.conversation.colors.Colorizer @@ -51,12 +55,33 @@ class PaymentMessageView @JvmOverloads constructor( val quoteViewColorTheme = QuoteViewColorTheme.resolveTheme(outgoing, false, recipient.hasWallpaper()) - binding.paymentAmount.setTextColor(quoteViewColorTheme.getForegroundColor(context)) - binding.paymentAmount.setMoney(payment.amount, 0L, currencyTypefaceSpan) + if (payment.state.isInProgress) { + binding.paymentAmount.visible = false + binding.paymentInprogress.visible = true + binding.paymentInprogress.setImageDrawable(getInProgressDrawable(quoteViewColorTheme.getForegroundColor(context))) + } else { + binding.paymentAmount.visible = true + binding.paymentInprogress.visible = false + binding.paymentAmount.setTextColor(quoteViewColorTheme.getForegroundColor(context)) + binding.paymentAmount.setMoney(payment.amount, 0L, currencyTypefaceSpan) + } ViewCompat.setBackgroundTintList(binding.paymentAmountLayout, ColorStateList.valueOf(quoteViewColorTheme.getBackgroundColor(context))) } + private fun getInProgressDrawable(@ColorInt color: Int): IndeterminateDrawable { + val spec = CircularProgressIndicatorSpec(context, null).apply { + indicatorInset = 0 + indicatorColors = intArrayOf(color) + indicatorSize = 20.dp + trackThickness = 2.dp + } + + val drawable = IndeterminateDrawable.createCircularDrawable(context, spec) + drawable.setBounds(0, 0, spec.indicatorSize, spec.indicatorSize) + return drawable + } + companion object { private val currencyTypefaceSpan = TypefaceSpan("sans-serif-light") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java index 511a77f950..1088305ba2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.java @@ -542,6 +542,20 @@ public abstract class MessageTable extends DatabaseTable implements MmsSmsColumn return CursorExtensionsKt.readToList(cursor, c -> CursorUtil.requireLong(c, THREAD_ID)); } + public @Nullable MessageId getPaymentMessage(@NonNull UUID paymentUuid) { + Cursor cursor = SQLiteDatabaseExtensionsKt.select(getReadableDatabase(), ID) + .from(getTableName()) + .where(getTypeField() + " & ? != 0 AND body = ?", Types.SPECIAL_TYPE_PAYMENTS_NOTIFICATION, paymentUuid) + .run(); + + long id = CursorExtensionsKt.readToSingleLong(cursor, -1); + if (id != -1) { + return new MessageId(id, getTableName().equals(MmsTable.TABLE_NAME)); + } else { + return null; + } + } + @Override public void remapRecipient(@NonNull RecipientId fromId, @NonNull RecipientId toId) { ContentValues values = new ContentValues(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsTable.java index 8f4452eefa..6afa3d7af7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsTable.java @@ -212,7 +212,8 @@ public class MmsTable extends MessageTable { "CREATE INDEX IF NOT EXISTS mms_parent_story_id_index ON " + TABLE_NAME + " (" + PARENT_STORY_ID + ");", "CREATE INDEX IF NOT EXISTS mms_thread_story_parent_story_index ON " + TABLE_NAME + " (" + THREAD_ID + ", " + DATE_RECEIVED + "," + STORY_TYPE + "," + PARENT_STORY_ID + ");", "CREATE INDEX IF NOT EXISTS mms_quote_id_quote_author_index ON " + TABLE_NAME + "(" + QUOTE_ID + ", " + QUOTE_AUTHOR + ");", - "CREATE INDEX IF NOT EXISTS mms_exported_index ON " + TABLE_NAME + " (" + EXPORTED + ");" + "CREATE INDEX IF NOT EXISTS mms_exported_index ON " + TABLE_NAME + " (" + EXPORTED + ");", + "CREATE INDEX IF NOT EXISTS mms_id_msg_box_payment_transactions_index ON " + TABLE_NAME + " (" + ID + "," + MESSAGE_BOX + ") WHERE " + MESSAGE_BOX + " & " + Types.SPECIAL_TYPE_PAYMENTS_NOTIFICATION + " != 0;" }; private static final String[] MMS_PROJECTION = new String[] { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/PaymentTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/PaymentTable.java index e7bb8cdf57..3516a30633 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/PaymentTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/PaymentTable.java @@ -18,6 +18,7 @@ import org.signal.core.util.CursorExtensionsKt; import org.signal.core.util.SQLiteDatabaseExtensionsKt; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; +import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.databaseprotos.CryptoValue; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; @@ -33,6 +34,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.Base64; import org.signal.core.util.CursorUtil; import org.signal.core.util.SqlUtil; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; import org.whispersystems.signalservice.api.payments.Money; import org.whispersystems.signalservice.api.util.UuidUtil; @@ -647,6 +649,10 @@ public final class PaymentTable extends DatabaseTable implements RecipientIdData private void notifyUuidChanged(@Nullable UUID uuid) { if (uuid != null) { ApplicationDependencies.getDatabaseObserver().notifyPaymentListeners(uuid); + MessageId messageId = SignalDatabase.mms().getPaymentMessage(uuid); + if (messageId != null) { + ApplicationDependencies.getDatabaseObserver().notifyMessageUpdateObservers(messageId); + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 7ca286c25e..3c52395e84 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V161_StorySendMessa import org.thoughtcrime.securesms.database.helpers.migration.V162_ThreadUnreadSelfMentionCountFixup import org.thoughtcrime.securesms.database.helpers.migration.V163_RemoteMegaphoneSnoozeSupportMigration import org.thoughtcrime.securesms.database.helpers.migration.V164_ThreadDatabaseReadIndexMigration +import org.thoughtcrime.securesms.database.helpers.migration.V165_MmsMessageBoxPaymentTransactionIndexMigration /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -28,7 +29,7 @@ object SignalDatabaseMigrations { val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass) - const val DATABASE_VERSION = 164 + const val DATABASE_VERSION = 165 @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { @@ -95,6 +96,10 @@ object SignalDatabaseMigrations { if (oldVersion < 164) { V164_ThreadDatabaseReadIndexMigration.migrate(context, db, oldVersion, newVersion) } + + if (oldVersion < 165) { + V165_MmsMessageBoxPaymentTransactionIndexMigration.migrate(context, db, oldVersion, newVersion) + } } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V165_MmsMessageBoxPaymentTransactionIndexMigration.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V165_MmsMessageBoxPaymentTransactionIndexMigration.kt new file mode 100644 index 0000000000..e4709d0cb4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V165_MmsMessageBoxPaymentTransactionIndexMigration.kt @@ -0,0 +1,15 @@ +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds an index to MMS table that only covers id and messages with the type of payment notification to + * speed up look ups for payment messages. + */ +@Suppress("ClassName") +object V165_MmsMessageBoxPaymentTransactionIndexMigration : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("CREATE INDEX IF NOT EXISTS mms_id_msg_box_payment_transactions_index ON mms (_id, msg_box) WHERE msg_box & 0x300000000 != 0") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java index 228cd13b63..39b7d68f96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PaymentNotificationSendJob.java @@ -42,24 +42,7 @@ public final class PaymentNotificationSendJob extends BaseJob { private final UUID uuid; public static Job create(@NonNull RecipientId recipientId, @NonNull UUID uuid, @NonNull String queue) { - if (FeatureFlags.paymentsInChatMessages()) { - return new PaymentNotificationSendJobV2(recipientId, uuid); - } else { - return new PaymentNotificationSendJob(recipientId, uuid, queue); - } - } - - private PaymentNotificationSendJob(@NonNull RecipientId recipientId, - @NonNull UUID uuid, - @NonNull String queue) - { - this(new Parameters.Builder() - .setQueue(queue) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - recipientId, - uuid); + return new PaymentNotificationSendJobV2(recipientId, uuid); } private PaymentNotificationSendJob(@NonNull Parameters parameters, diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index 237b60fe10..2001aea206 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -465,21 +465,19 @@ public final class MessageContentProcessor { Money.MobileCoin.ZERO, Money.MobileCoin.ZERO, paymentNotification.getReceipt(), - FeatureFlags.paymentsInChatMessages()); + true); - if (FeatureFlags.paymentsInChatMessages()) { - IncomingMediaMessage mediaMessage = IncomingMediaMessage.createIncomingPaymentNotification(senderRecipient.getId(), - content, - receivedTime, - TimeUnit.SECONDS.toMillis(message.getExpiresInSeconds()), - uuid); + IncomingMediaMessage mediaMessage = IncomingMediaMessage.createIncomingPaymentNotification(senderRecipient.getId(), + content, + receivedTime, + TimeUnit.SECONDS.toMillis(message.getExpiresInSeconds()), + uuid); - Optional insertResult = SignalDatabase.mms().insertSecureDecryptedMessageInbox(mediaMessage, -1); - smsMessageId.ifPresent(smsId -> SignalDatabase.sms().deleteMessage(smsId)); - if (insertResult.isPresent()) { - messageId = new MessageId(insertResult.get().getMessageId(), true); - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.get().getThreadId())); - } + Optional insertResult = SignalDatabase.mms().insertSecureDecryptedMessageInbox(mediaMessage, -1); + smsMessageId.ifPresent(smsId -> SignalDatabase.sms().deleteMessage(smsId)); + if (insertResult.isPresent()) { + messageId = new MessageId(insertResult.get().getMessageId(), true); + ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.get().getThreadId())); } } catch (PaymentTable.PublicKeyConflictException e) { warn(content.getTimestamp(), "Ignoring payment with public key already in database"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 52714e7df0..4589b26190 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -105,7 +105,6 @@ public final class FeatureFlags { public static final String CREDIT_CARD_DISABLED_REGIONS = "global.donations.ccDisabledRegions"; public static final String PAYPAL_DISABLED_REGIONS = "global.donations.paypalDisabledRegions"; private static final String CDS_HARD_LIMIT = "android.cds.hardLimit"; - private static final String PAYMENTS_IN_CHAT_MESSAGES = "android.payments.inChatMessages"; private static final String CHAT_FILTERS = "android.chat.filters"; private static final String PAYPAL_DONATIONS = "android.donations.paypal"; @@ -166,7 +165,6 @@ public final class FeatureFlags { PAYPAL_DISABLED_REGIONS, KEEP_MUTED_CHATS_ARCHIVED, CDS_HARD_LIMIT, - PAYMENTS_IN_CHAT_MESSAGES, CHAT_FILTERS, PAYPAL_DONATIONS ); @@ -232,8 +230,7 @@ public final class FeatureFlags { CREDIT_CARD_PAYMENTS, PAYMENTS_REQUEST_ACTIVATE_FLOW, KEEP_MUTED_CHATS_ARCHIVED, - CDS_HARD_LIMIT, - PAYMENTS_IN_CHAT_MESSAGES + CDS_HARD_LIMIT ); /** @@ -550,11 +547,6 @@ public final class FeatureFlags { return getBoolean(PAYMENTS_REQUEST_ACTIVATE_FLOW, false); } - /** Whether client supports processing a payment notification as a in-chat message */ - public static boolean paymentsInChatMessages() { - return getBoolean(PAYMENTS_IN_CHAT_MESSAGES, false); - } - /** * Whether users can enable keeping conversations with incoming messages archived if the conversation is muted. */ diff --git a/app/src/main/res/layout/payment_message_view.xml b/app/src/main/res/layout/payment_message_view.xml index cc6689c9d0..7b30af8611 100644 --- a/app/src/main/res/layout/payment_message_view.xml +++ b/app/src/main/res/layout/payment_message_view.xml @@ -36,6 +36,12 @@ app:autoSizeTextType="uniform" app:money="MOB:275000000000000" /> + +