From d406e8f5b869a47ab8d5ce2c5581166928bf1a9b Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 30 Jul 2024 10:39:10 -0300 Subject: [PATCH] Add support for several backup alert sheets. --- .../backup/v2/ui/BackupAlertBottomSheet.kt | 22 ++- .../settings/app/AppSettingsFragment.kt | 4 +- .../subscription/InAppPaymentsRepository.kt | 36 ++++ .../InAppPaymentsBottomSheetDelegate.kt | 162 ++++++++++++++++++ .../completed/TerminalDonationDelegate.kt | 89 ---------- .../manage/ManageDonationsFragment.kt | 4 +- .../ConversationListFragment.java | 4 +- .../securesms/keyvalue/InAppPaymentValues.kt | 7 +- app/src/main/res/values/strings.xml | 8 + 9 files changed, 237 insertions(+), 99 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt index 4a0c37803f..87f6406ad7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.core.os.BundleCompat import androidx.core.os.bundleOf +import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.parcelize.Parcelize import org.signal.core.ui.BottomSheets import org.signal.core.ui.Buttons @@ -82,8 +83,7 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() { } BackupAlert.PAYMENT_PROCESSING -> Unit BackupAlert.MEDIA_BACKUPS_ARE_OFF, BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> { - // TODO [message-backups] -- We need to force this to download everything. - AppDependencies.jobManager.add(BackupRestoreMediaJob()) + performFullMediaDownload() } BackupAlert.DISK_FULL -> Unit } @@ -102,13 +102,29 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() { // TODO [message-backups] - Silence and remind on last day } BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> { - // TODO [message-backups] - Silence forever + displayLastChanceDialog() } BackupAlert.DISK_FULL -> Unit } dismissAllowingStateLoss() } + + private fun displayLastChanceDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.BackupAlertBottomSheet__media_will_be_deleted) + .setMessage(R.string.BackupAlertBottomSheet__the_media_stored_in_your_backup) + .setPositiveButton(R.string.BackupAlertBottomSheet__download) { _, _ -> + performFullMediaDownload() + } + .setNegativeButton(R.string.BackupAlertBottomSheet__dont_download, null) + .show() + } + + private fun performFullMediaDownload() { + // TODO [message-backups] -- We need to force this to download everything + AppDependencies.jobManager.add(BackupRestoreMediaJob()) + } } @Composable diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt index 29afe03212..049167a50c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsFragment.kt @@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.PreferenceModel import org.thoughtcrime.securesms.components.settings.PreferenceViewHolder import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository -import org.thoughtcrime.securesms.components.settings.app.subscription.completed.TerminalDonationDelegate +import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord import org.thoughtcrime.securesms.events.ReminderUpdateEvent @@ -61,7 +61,7 @@ class AppSettingsFragment : DSLSettingsFragment( private lateinit var reminderView: Stub override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - viewLifecycleOwner.lifecycle.addObserver(TerminalDonationDelegate(childFragmentManager, viewLifecycleOwner)) + viewLifecycleOwner.lifecycle.addObserver(InAppPaymentsBottomSheetDelegate(childFragmentManager, viewLifecycleOwner)) super.onViewCreated(view, savedInstanceState) reminderView = ViewUtil.findStubById(view, R.id.reminder_stub) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt index 3805ad819c..9f515c1763 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentsRepository.kt @@ -53,6 +53,7 @@ import java.util.Optional import kotlin.jvm.optionals.getOrNull import kotlin.time.Duration import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds /** @@ -63,6 +64,9 @@ object InAppPaymentsRepository { private const val JOB_PREFIX = "InAppPayments__" private val TAG = Log.tag(InAppPaymentsRepository::class.java) + private val backupExpirationTimeout = 30.days + private val backupExpirationDeletion = 60.days + private val temporaryErrorProcessor = PublishProcessor.create>() /** @@ -347,6 +351,32 @@ object InAppPaymentsRepository { } } + /** + * Determines if we are in the timeout period to display the "your backup will be deleted today" message + */ + @WorkerThread + fun getExpiredBackupDeletionState(): ExpiredBackupDeletionState { + val inAppPayment = SignalDatabase.inAppPayments.getByLatestEndOfPeriod(InAppPaymentType.RECURRING_BACKUP) + if (inAppPayment == null) { + Log.w(TAG, "InAppPayment for recurring backup not found for last day check. Clearing check.") + SignalStore.inAppPayments.showLastDayToDownloadMediaDialog = false + return ExpiredBackupDeletionState.NONE + } + + val now = SignalStore.misc.estimatedServerTime.milliseconds + val lastEndOfPeriod = inAppPayment.endOfPeriod + val displayDialogStart = lastEndOfPeriod + backupExpirationTimeout + val displayDialogEnd = lastEndOfPeriod + backupExpirationDeletion + + return if (now in displayDialogStart..displayDialogEnd) { + ExpiredBackupDeletionState.DELETE_TODAY + } else if (now > displayDialogEnd) { + ExpiredBackupDeletionState.EXPIRED + } else { + ExpiredBackupDeletionState.NONE + } + } + @JvmStatic @WorkerThread fun setShouldCancelSubscriptionBeforeNextSubscribeAttempt(subscriber: InAppPaymentSubscriberRecord, shouldCancel: Boolean) { @@ -623,4 +653,10 @@ object InAppPaymentsRepository { InAppPaymentData.PaymentMethodType.PAYPAL -> DonationProcessor.PAYPAL } } + + enum class ExpiredBackupDeletionState { + NONE, + DELETE_TODAY, + EXPIRED + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt new file mode 100644 index 0000000000..afe6f1659a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/InAppPaymentsBottomSheetDelegate.kt @@ -0,0 +1,162 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.subscription.completed + +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.kotlin.subscribeBy +import io.reactivex.rxjava3.schedulers.Schedulers +import org.signal.core.util.concurrent.LifecycleDisposable +import org.thoughtcrime.securesms.backup.v2.ui.BackupAlert +import org.thoughtcrime.securesms.backup.v2.ui.BackupAlertBottomSheet +import org.thoughtcrime.securesms.badges.Badges +import org.thoughtcrime.securesms.badges.self.expired.MonthlyDonationCanceledBottomSheetDialogFragment +import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheet +import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheetArgs +import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository +import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragment +import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragmentArgs +import org.thoughtcrime.securesms.database.InAppPaymentTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord +import org.thoughtcrime.securesms.database.model.databaseprotos.DonationErrorValue +import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData +import org.thoughtcrime.securesms.keyvalue.SignalStore + +/** + * Handles displaying bottom sheets for in-app payments. The current policy is to "fire and forget". + */ +class InAppPaymentsBottomSheetDelegate( + private val fragmentManager: FragmentManager, + private val lifecycleOwner: LifecycleOwner, + private vararg val supportedTypes: InAppPaymentSubscriberRecord.Type = arrayOf(InAppPaymentSubscriberRecord.Type.DONATION) +) : DefaultLifecycleObserver { + + companion object { + + private val inAppPaymentProcessingErrors = listOf( + InAppPaymentData.Error.Type.PAYMENT_PROCESSING, + InAppPaymentData.Error.Type.STRIPE_FAILURE, + InAppPaymentData.Error.Type.STRIPE_CODED_ERROR, + InAppPaymentData.Error.Type.STRIPE_DECLINED_ERROR, + InAppPaymentData.Error.Type.PAYPAL_CODED_ERROR, + InAppPaymentData.Error.Type.PAYPAL_DECLINED_ERROR + ) + } + + private val lifecycleDisposable = LifecycleDisposable().apply { + bindTo(lifecycleOwner) + } + + private val badgeRepository = TerminalDonationRepository() + + override fun onResume(owner: LifecycleOwner) { + if (InAppPaymentSubscriberRecord.Type.DONATION in supportedTypes) { + handleLegacyTerminalDonationSheets() + handleLegacyVerifiedMonthlyDonationSheets() + handleInAppPaymentDonationSheets() + } + + if (InAppPaymentSubscriberRecord.Type.BACKUP in supportedTypes) { + handleInAppPaymentBackupsSheets() + } + } + + /** + * Handles terminal donations consumed from the InAppPayments values. These are only ever set by the legacy jobs, + * and will be completely removed close to when the jobs are removed. (We might want an additional 90 days?) + */ + private fun handleLegacyTerminalDonationSheets() { + val donations = SignalStore.inAppPayments.consumeTerminalDonations() + for (donation in donations) { + if (donation.isLongRunningPaymentMethod && (donation.error == null || donation.error.type != DonationErrorValue.Type.REDEMPTION)) { + TerminalDonationBottomSheet.show(fragmentManager, donation) + } else if (donation.error != null) { + lifecycleDisposable += badgeRepository.getBadge(donation).observeOn(AndroidSchedulers.mainThread()).subscribe { badge -> + val args = ThanksForYourSupportBottomSheetDialogFragmentArgs.Builder(badge).build().toBundle() + val sheet = ThanksForYourSupportBottomSheetDialogFragment() + + sheet.arguments = args + sheet.show(fragmentManager, null) + } + } + } + } + + /** + * Handles the 'verified' sheet that appears after a user externally verifies a payment and returns to the application. + * These are only ever set by the legacy jobs, and will be completely removed close to when the jobs are removed. (We might + * want an additional 90 days?) + */ + private fun handleLegacyVerifiedMonthlyDonationSheets() { + SignalStore.inAppPayments.consumeVerifiedSubscription3DSData()?.also { + DonationPendingBottomSheet().apply { + arguments = DonationPendingBottomSheetArgs.Builder(it.inAppPayment).build().toBundle() + }.show(fragmentManager, null) + } + } + + /** + * Handles the new in-app payment sheets for donations. + */ + private fun handleInAppPaymentDonationSheets() { + lifecycleDisposable += Single.fromCallable { + SignalDatabase.inAppPayments.consumeDonationPaymentsToNotifyUser() + }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeBy { inAppPayments -> + for (payment in inAppPayments) { + if (payment.data.error == null && payment.state == InAppPaymentTable.State.END) { + ThanksForYourSupportBottomSheetDialogFragment() + .apply { arguments = ThanksForYourSupportBottomSheetDialogFragmentArgs.Builder(Badges.fromDatabaseBadge(payment.data.badge!!)).build().toBundle() } + .show(fragmentManager, null) + } else if (payment.data.error != null && payment.state == InAppPaymentTable.State.PENDING) { + DonationPendingBottomSheet().apply { + arguments = DonationPendingBottomSheetArgs.Builder(payment).build().toBundle() + }.show(fragmentManager, null) + } else if (isUnexpectedCancellation(payment.state, payment.data) && SignalStore.inAppPayments.showMonthlyDonationCanceledDialog) { + MonthlyDonationCanceledBottomSheetDialogFragment.show(fragmentManager) + } + } + } + } + + /** + * Handles the new in-app payment sheets for backups. + */ + private fun handleInAppPaymentBackupsSheets() { + lifecycleDisposable += Single.fromCallable { + SignalDatabase.inAppPayments.consumeBackupPaymentsToNotifyUser() + }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeBy { inAppPayments -> + for (payment in inAppPayments) { + if (isPaymentProcessingError(payment.state, payment.data)) { + BackupAlertBottomSheet.create(BackupAlert.COULD_NOT_COMPLETE_BACKUP).show(fragmentManager, null) + } else if (isUnexpectedCancellation(payment.state, payment.data)) { + BackupAlertBottomSheet.create(BackupAlert.MEDIA_BACKUPS_ARE_OFF).show(fragmentManager, null) + } + } + } + + if (SignalStore.inAppPayments.showLastDayToDownloadMediaDialog) { + lifecycleDisposable += Single.fromCallable { + InAppPaymentsRepository.getExpiredBackupDeletionState() + }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeBy { + if (it == InAppPaymentsRepository.ExpiredBackupDeletionState.DELETE_TODAY) { + BackupAlertBottomSheet.create(BackupAlert.MEDIA_WILL_BE_DELETED_TODAY).show(fragmentManager, null) + } + } + } + } + + private fun isUnexpectedCancellation(inAppPaymentState: InAppPaymentTable.State, inAppPaymentData: InAppPaymentData): Boolean { + return inAppPaymentState == InAppPaymentTable.State.END && inAppPaymentData.error != null && inAppPaymentData.cancellation != null && inAppPaymentData.cancellation.reason != InAppPaymentData.Cancellation.Reason.MANUAL + } + + private fun isPaymentProcessingError(inAppPaymentState: InAppPaymentTable.State, inAppPaymentData: InAppPaymentData): Boolean { + return inAppPaymentState == InAppPaymentTable.State.END && inAppPaymentData.error != null && (inAppPaymentData.error.type in inAppPaymentProcessingErrors) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate.kt deleted file mode 100644 index a475a2b87d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.settings.app.subscription.completed - -import androidx.fragment.app.FragmentManager -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Single -import io.reactivex.rxjava3.kotlin.subscribeBy -import io.reactivex.rxjava3.schedulers.Schedulers -import org.signal.core.util.concurrent.LifecycleDisposable -import org.thoughtcrime.securesms.badges.Badges -import org.thoughtcrime.securesms.badges.self.expired.MonthlyDonationCanceledBottomSheetDialogFragment -import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheet -import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheetArgs -import org.thoughtcrime.securesms.components.settings.app.subscription.donate.stripe.Stripe3DSData -import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragment -import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragmentArgs -import org.thoughtcrime.securesms.database.InAppPaymentTable -import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.database.model.databaseprotos.DonationErrorValue -import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData -import org.thoughtcrime.securesms.keyvalue.SignalStore - -/** - * Handles displaying the "Thank You" or "Donation completed" sheet when the user navigates to an appropriate screen. - * These sheets are one-shot. - */ -class TerminalDonationDelegate( - private val fragmentManager: FragmentManager, - private val lifecycleOwner: LifecycleOwner -) : DefaultLifecycleObserver { - - private val lifecycleDisposable = LifecycleDisposable().apply { - bindTo(lifecycleOwner) - } - - private val badgeRepository = TerminalDonationRepository() - - override fun onResume(owner: LifecycleOwner) { - val donations = SignalStore.inAppPayments.consumeTerminalDonations() - for (donation in donations) { - if (donation.isLongRunningPaymentMethod && (donation.error == null || donation.error.type != DonationErrorValue.Type.REDEMPTION)) { - TerminalDonationBottomSheet.show(fragmentManager, donation) - } else if (donation.error != null) { - lifecycleDisposable += badgeRepository.getBadge(donation).observeOn(AndroidSchedulers.mainThread()).subscribe { badge -> - val args = ThanksForYourSupportBottomSheetDialogFragmentArgs.Builder(badge).build().toBundle() - val sheet = ThanksForYourSupportBottomSheetDialogFragment() - - sheet.arguments = args - sheet.show(fragmentManager, null) - } - } - } - - val verifiedMonthlyDonation: Stripe3DSData? = SignalStore.inAppPayments.consumeVerifiedSubscription3DSData() - if (verifiedMonthlyDonation != null) { - DonationPendingBottomSheet().apply { - arguments = DonationPendingBottomSheetArgs.Builder(verifiedMonthlyDonation.inAppPayment).build().toBundle() - }.show(fragmentManager, null) - } - - handleInAppPaymentSheets() - } - - private fun handleInAppPaymentSheets() { - lifecycleDisposable += Single.fromCallable { - SignalDatabase.inAppPayments.consumeDonationPaymentsToNotifyUser() - }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribeBy { inAppPayments -> - for (payment in inAppPayments) { - if (payment.data.error == null && payment.state == InAppPaymentTable.State.END) { - ThanksForYourSupportBottomSheetDialogFragment() - .apply { arguments = ThanksForYourSupportBottomSheetDialogFragmentArgs.Builder(Badges.fromDatabaseBadge(payment.data.badge!!)).build().toBundle() } - .show(fragmentManager, null) - } else if (payment.data.error != null && payment.state == InAppPaymentTable.State.PENDING) { - DonationPendingBottomSheet().apply { - arguments = DonationPendingBottomSheetArgs.Builder(payment).build().toBundle() - }.show(fragmentManager, null) - } else if (payment.data.error != null && payment.data.cancellation != null && payment.data.cancellation.reason != InAppPaymentData.Cancellation.Reason.MANUAL && SignalStore.inAppPayments.showMonthlyDonationCanceledDialog) { - MonthlyDonationCanceledBottomSheetDialogFragment.show(fragmentManager) - } - } - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt index b0028539d2..d610065a08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt @@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatMoney -import org.thoughtcrime.securesms.components.settings.app.subscription.completed.TerminalDonationDelegate +import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate import org.thoughtcrime.securesms.components.settings.app.subscription.models.NetworkFailure import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.components.settings.models.IndeterminateLoadingCircle @@ -67,7 +67,7 @@ class ManageDonationsFragment : private val viewModel: ManageDonationsViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - viewLifecycleOwner.lifecycle.addObserver(TerminalDonationDelegate(childFragmentManager, viewLifecycleOwner)) + viewLifecycleOwner.lifecycle.addObserver(InAppPaymentsBottomSheetDelegate(childFragmentManager, viewLifecycleOwner)) super.onViewCreated(view, savedInstanceState) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index fdeb5c1f09..860eb346c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -114,7 +114,7 @@ import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder; import org.thoughtcrime.securesms.components.reminder.UsernameOutOfSyncReminder; import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity; import org.thoughtcrime.securesms.components.settings.app.notifications.manual.NotificationProfileSelectionFragment; -import org.thoughtcrime.securesms.components.settings.app.subscription.completed.TerminalDonationDelegate; +import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate; import org.thoughtcrime.securesms.components.settings.app.subscription.errors.UnexpectedSubscriptionCancellation; import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner; @@ -277,7 +277,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - getViewLifecycleOwner().getLifecycle().addObserver(new TerminalDonationDelegate(getParentFragmentManager(), getViewLifecycleOwner())); + getViewLifecycleOwner().getLifecycle().addObserver(new InAppPaymentsBottomSheetDelegate(getParentFragmentManager(), getViewLifecycleOwner())); BackupAlertDelegate.delegate(getParentFragmentManager(), getViewLifecycleOwner().getLifecycle()); lifecycleDisposable = new LifecycleDisposable(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt index 04136b724f..e2419695ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InAppPaymentValues.kt @@ -70,6 +70,7 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor private const val SUBSCRIPTION_CANCELATION_TIMESTAMP = "donation.subscription.cancelation.timestamp" private const val SUBSCRIPTION_CANCELATION_WATERMARK = "donation.subscription.cancelation.watermark" private const val SHOW_CANT_PROCESS_DIALOG = "show.cant.process.dialog" + private const val SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG = "inapppayment.show.last.day.to.download.media.dialog" /** * The current request context for subscription. This should be stored until either @@ -160,7 +161,8 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor SUBSCRIPTION_EOP_STARTED_TO_CONVERT, SUBSCRIPTION_EOP_STARTED_TO_REDEEM, SUBSCRIPTION_EOP_REDEEMED, - SUBSCRIPTION_PAYMENT_SOURCE_TYPE + SUBSCRIPTION_PAYMENT_SOURCE_TYPE, + SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG ) private val recurringDonationCurrencyPublisher: Subject by lazy { BehaviorSubject.createDefault(getSubscriptionCurrency(InAppPaymentSubscriberRecord.Type.DONATION)) } @@ -417,6 +419,9 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor @get:JvmName("showCantProcessDialog") var showMonthlyDonationCanceledDialog: Boolean by booleanValue(SHOW_CANT_PROCESS_DIALOG, true) + @get:JvmName("showLastDayToDownloadMediaDialog") + var showLastDayToDownloadMediaDialog: Boolean by booleanValue(SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG, false) + /** * Denotes that the previous attempt to subscribe failed in some way. Either an * automatic renewal failed resulting in an unexpected expiration, or payment failed diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc1f9471e6..1453abdf22 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7149,6 +7149,14 @@ Download later Don\'t download media + + Media will be deleted + + The media stored in your backup will be permanently deleted today. This is your last chance to download it. + + Download + + Don\'t download