From 0d4e109c72dbb1830d00c9352ac4be5867e28ac8 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 19 Nov 2021 08:33:04 -0400 Subject: [PATCH] Implement several badge job tweaks to align with iOS. --- .../jobs/BoostReceiptRequestResponseJob.java | 18 ++++-- .../jobs/DonationReceiptRedemptionJob.java | 20 +++++-- ...SubscriptionReceiptRequestResponseJob.java | 6 +- .../notifications/NotificationIds.java | 1 + ...fication.kt => DonorBadgeNotifications.kt} | 56 +++++++++---------- 5 files changed, 61 insertions(+), 40 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/subscription/{SubscriptionNotification.kt => DonorBadgeNotifications.kt} (90%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java index 229741a9b4..da25f820e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java @@ -1,5 +1,7 @@ package org.thoughtcrime.securesms.jobs; +import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -19,7 +21,7 @@ import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.subscription.SubscriptionNotification; +import org.thoughtcrime.securesms.subscription.DonorBadgeNotifications; import org.whispersystems.signalservice.internal.ServiceResponse; import java.io.IOException; @@ -43,13 +45,15 @@ public class BoostReceiptRequestResponseJob extends BaseJob { private final String paymentIntentId; + private boolean isPaymentFailure; + static BoostReceiptRequestResponseJob createJob(StripeApi.PaymentIntent paymentIntent) { return new BoostReceiptRequestResponseJob( new Parameters .Builder() .addConstraint(NetworkConstraint.KEY) .setQueue("BoostReceiptRedemption") - .setLifespan(TimeUnit.DAYS.toMillis(30)) + .setLifespan(TimeUnit.DAYS.toMillis(1)) .setMaxAttempts(Parameters.UNLIMITED) .build(), null, @@ -95,7 +99,7 @@ public class BoostReceiptRequestResponseJob extends BaseJob { @Override public void onFailure() { - SubscriptionNotification.VerificationFailed.INSTANCE.show(context); + DonorBadgeNotifications.RedemptionFailed.INSTANCE.show(context); } @Override @@ -118,6 +122,7 @@ public class BoostReceiptRequestResponseJob extends BaseJob { if (response.getApplicationError().isPresent()) { handleApplicationError(response); + setOutputData(new Data.Builder().putBoolean(DonationReceiptRedemptionJob.INPUT_PAYMENT_FAILURE, true).build()); } else if (response.getResult().isPresent()) { ReceiptCredential receiptCredential = getReceiptCredential(response.getResult().get()); @@ -139,11 +144,14 @@ public class BoostReceiptRequestResponseJob extends BaseJob { Throwable applicationException = response.getApplicationError().get(); switch (response.getStatus()) { case 204: - Log.w(TAG, "User does not have receipts available to exchange. Exiting.", applicationException, true); - break; + Log.w(TAG, "User payment not be completed yet.", applicationException, true); + throw new RetryableException(); case 400: Log.w(TAG, "Receipt credential request failed to validate.", applicationException, true); throw new Exception(applicationException); + case 402: + Log.w(TAG, "User payment failed.", applicationException, true); + break; case 409: Log.w(TAG, "Receipt already redeemed with a different request credential.", response.getApplicationError().get(), true); throw new Exception(applicationException); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java index 139e237db2..15d4003670 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java @@ -9,7 +9,7 @@ import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.subscription.SubscriptionNotification; +import org.thoughtcrime.securesms.subscription.DonorBadgeNotifications; import org.whispersystems.signalservice.internal.EmptyResponse; import org.whispersystems.signalservice.internal.ServiceResponse; @@ -27,6 +27,7 @@ public class DonationReceiptRedemptionJob extends BaseJob { public static final String SUBSCRIPTION_QUEUE = "ReceiptRedemption"; public static final String KEY = "DonationReceiptRedemptionJob"; public static final String INPUT_RECEIPT_CREDENTIAL_PRESENTATION = "data.receipt.credential.presentation"; + public static final String INPUT_PAYMENT_FAILURE = "data.payment.failure"; public static DonationReceiptRedemptionJob createJobForSubscription() { return new DonationReceiptRedemptionJob( @@ -36,7 +37,7 @@ public class DonationReceiptRedemptionJob extends BaseJob { .setQueue(SUBSCRIPTION_QUEUE) .setMaxAttempts(Parameters.UNLIMITED) .setMaxInstancesForQueue(1) - .setLifespan(TimeUnit.DAYS.toMillis(7)) + .setLifespan(TimeUnit.DAYS.toMillis(1)) .build()); } @@ -47,7 +48,7 @@ public class DonationReceiptRedemptionJob extends BaseJob { .addConstraint(NetworkConstraint.KEY) .setQueue("BoostReceiptRedemption") .setMaxAttempts(Parameters.UNLIMITED) - .setLifespan(TimeUnit.DAYS.toMillis(30)) + .setLifespan(TimeUnit.DAYS.toMillis(1)) .build()); } @@ -67,7 +68,14 @@ public class DonationReceiptRedemptionJob extends BaseJob { @Override public void onFailure() { - SubscriptionNotification.RedemptionFailed.INSTANCE.show(context); + Data inputData = getInputData(); + + if (inputData != null && inputData.getBooleanOrDefault(INPUT_PAYMENT_FAILURE, false)) { + DonorBadgeNotifications.PaymentFailed.INSTANCE.show(context); + } else { + DonorBadgeNotifications.RedemptionFailed.INSTANCE.show(context); + } + if (isForSubscription()) { SignalStore.donationsValues().markSubscriptionRedemptionFailed(); } @@ -82,6 +90,10 @@ public class DonationReceiptRedemptionJob extends BaseJob { return; } + if (inputData.getBooleanOrDefault(INPUT_PAYMENT_FAILURE, false)) { + throw new Exception("Payment failed."); + } + byte[] presentationBytes = inputData.getStringAsBlob(INPUT_RECEIPT_CREDENTIAL_PRESENTATION); if (presentationBytes == null) { Log.d(TAG, "No response data. Exiting.", null, true); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java index 5a26059537..c7a6d57ec7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java @@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.subscription.Subscriber; -import org.thoughtcrime.securesms.subscription.SubscriptionNotification; +import org.thoughtcrime.securesms.subscription.DonorBadgeNotifications; import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription; import org.whispersystems.signalservice.api.subscriptions.SubscriberId; import org.whispersystems.signalservice.internal.ServiceResponse; @@ -55,7 +55,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { .addConstraint(NetworkConstraint.KEY) .setQueue("ReceiptRedemption") .setMaxInstancesForQueue(1) - .setLifespan(TimeUnit.DAYS.toMillis(7)) + .setLifespan(TimeUnit.DAYS.toMillis(1)) .setMaxAttempts(Parameters.UNLIMITED) .build(), null, @@ -102,7 +102,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { @Override public void onFailure() { - SubscriptionNotification.VerificationFailed.INSTANCE.show(context); + DonorBadgeNotifications.RedemptionFailed.INSTANCE.show(context); SignalStore.donationsValues().markSubscriptionRedemptionFailed(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java index c5c6db2a00..5c2c7af29a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java @@ -14,6 +14,7 @@ public final class NotificationIds { public static final int USER_NOTIFICATION_MIGRATION = 525600; public static final int DEVICE_TRANSFER = 625420; public static final int SUBSCRIPTION_VERIFY_FAILED = 630001; + public static final int BOOST_PAYMENT_FAILED = 630002; private NotificationIds() { } diff --git a/app/src/main/java/org/thoughtcrime/securesms/subscription/SubscriptionNotification.kt b/app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt similarity index 90% rename from app/src/main/java/org/thoughtcrime/securesms/subscription/SubscriptionNotification.kt rename to app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt index 819efd8e99..522a39d39c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/subscription/SubscriptionNotification.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/subscription/DonorBadgeNotifications.kt @@ -11,34 +11,8 @@ import org.thoughtcrime.securesms.help.HelpFragment import org.thoughtcrime.securesms.notifications.NotificationChannels import org.thoughtcrime.securesms.notifications.NotificationIds -sealed class SubscriptionNotification { - object VerificationFailed : SubscriptionNotification() { - override fun show(context: Context) { - val notification = NotificationCompat.Builder(context, NotificationChannels.FAILURES) - .setSmallIcon(R.drawable.ic_notification) - .setContentTitle(context.getString(R.string.Subscription__verification_failed)) - .setContentText(context.getString(R.string.Subscription__please_contact_support_for_more_information)) - .addAction( - NotificationCompat.Action.Builder( - null, - context.getString(R.string.Subscription__contact_support), - PendingIntent.getActivity( - context, - 0, - AppSettingsActivity.help(context, HelpFragment.DONATION_INDEX), - if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_ONE_SHOT else 0 - ) - ).build() - ) - .build() - - NotificationManagerCompat - .from(context) - .notify(NotificationIds.SUBSCRIPTION_VERIFY_FAILED, notification) - } - } - - object RedemptionFailed : SubscriptionNotification() { +sealed class DonorBadgeNotifications { + object RedemptionFailed : DonorBadgeNotifications() { override fun show(context: Context) { val notification = NotificationCompat.Builder(context, NotificationChannels.FAILURES) .setSmallIcon(R.drawable.ic_notification) @@ -64,5 +38,31 @@ sealed class SubscriptionNotification { } } + object PaymentFailed : DonorBadgeNotifications() { + override fun show(context: Context) { + val notification = NotificationCompat.Builder(context, NotificationChannels.FAILURES) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(context.getString(R.string.DonationsErrors__payment_failed)) + .setContentText(context.getString(R.string.Subscription__please_contact_support_for_more_information)) + .addAction( + NotificationCompat.Action.Builder( + null, + context.getString(R.string.Subscription__contact_support), + PendingIntent.getActivity( + context, + 0, + AppSettingsActivity.help(context, HelpFragment.DONATION_INDEX), + if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_ONE_SHOT else 0 + ) + ).build() + ) + .build() + + NotificationManagerCompat + .from(context) + .notify(NotificationIds.SUBSCRIPTION_VERIFY_FAILED, notification) + } + } + abstract fun show(context: Context) }