From 61f91d6b83d3c603c8d01e2a0e91ae41345466b4 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 14 Nov 2024 10:38:28 -0400 Subject: [PATCH] Add MediaTTL to subscriptions configuration and wire in. --- .../securesms/backup/v2/BackupRepository.kt | 5 +++- .../backup/v2/ui/BackupAlertBottomSheet.kt | 26 ++++++++++++------- .../v2/ui/subscription/MessageBackupsType.kt | 4 ++- .../MessageBackupsTypeSelectionScreen.kt | 4 ++- .../app/backups/BackupsSettingsFragment.kt | 7 +++-- .../app/backups/BackupsSettingsViewModel.kt | 3 ++- .../remote/RemoteBackupsSettingsFragment.kt | 15 +++++++---- .../push/SubscriptionsConfiguration.java | 7 +++++ 8 files changed, 51 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index a95b88c2f1..432d6ca389 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -99,6 +99,7 @@ import java.io.OutputStream import java.time.ZonedDateTime import java.util.Locale import java.util.concurrent.atomic.AtomicLong +import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.milliseconds import org.signal.libsignal.messagebackup.MessageBackupKey as LibSignalMessageBackupKey @@ -1147,10 +1148,12 @@ object BackupRepository { private suspend fun getPaidType(): MessageBackupsType? { val config = getSubscriptionsConfiguration() val product = AppDependencies.billingApi.queryProduct() ?: return null + val backupLevelConfiguration = config.backupConfiguration.backupLevelConfigurationMap[SubscriptionsConfiguration.BACKUPS_LEVEL] ?: return null return MessageBackupsType.Paid( pricePerMonth = product.price, - storageAllowanceBytes = config.backupConfiguration.backupLevelConfigurationMap[SubscriptionsConfiguration.BACKUPS_LEVEL]!!.storageAllowanceBytes + storageAllowanceBytes = backupLevelConfiguration.storageAllowanceBytes, + mediaTtl = backupLevelConfiguration.mediaTtlDays.days ) } 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 f64515919f..9d727ad10f 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 @@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob import org.thoughtcrime.securesms.payments.FiatMoneyUtil +import kotlin.time.Duration import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.milliseconds import org.signal.core.ui.R as CoreUiR @@ -102,6 +103,7 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() { BackupAlertSheetContent( backupAlert = backupAlert, isSubscribeEnabled = isSubscribeEnabled, + mediaTtl = paidBackupType.mediaTtl, onPrimaryActionClick = performPrimaryAction, onSecondaryActionClick = this::performSecondaryAction ) @@ -195,6 +197,7 @@ private fun BackupAlertSheetContent( backupAlert: BackupAlert, pricePerMonth: String = "", isSubscribeEnabled: Boolean = true, + mediaTtl: Duration, onPrimaryActionClick: () -> Unit = {}, onSecondaryActionClick: () -> Unit = {} ) { @@ -254,7 +257,7 @@ private fun BackupAlertSheetContent( ) BackupAlert.FailedToRenew -> PaymentProcessingBody() - is BackupAlert.MediaBackupsAreOff -> MediaBackupsAreOffBody(backupAlert.endOfPeriodSeconds) + is BackupAlert.MediaBackupsAreOff -> MediaBackupsAreOffBody(backupAlert.endOfPeriodSeconds, mediaTtl) BackupAlert.MediaWillBeDeletedToday -> MediaWillBeDeletedTodayBody() is BackupAlert.DiskFull -> DiskFullBody(requiredSpace = backupAlert.requiredSpace) } @@ -308,10 +311,10 @@ private fun PaymentProcessingBody() { @Composable private fun MediaBackupsAreOffBody( - endOfPeriodSeconds: Long + endOfPeriodSeconds: Long, + mediaTtl: Duration ) { - // TODO [backups] Get value from config to calculate days until deletion. - val daysUntilDeletion = remember { endOfPeriodSeconds.days + 60.days }.inWholeDays.toInt() + val daysUntilDeletion = remember { endOfPeriodSeconds.days + mediaTtl }.inWholeDays.toInt() Text( text = pluralStringResource(id = R.plurals.BackupAlertBottomSheet__your_backup_plan_has_expired, daysUntilDeletion, daysUntilDeletion), @@ -416,7 +419,8 @@ private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int { private fun BackupAlertSheetContentPreviewGeneric() { Previews.BottomSheetPreview { BackupAlertSheetContent( - backupAlert = BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = 7) + backupAlert = BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = 7), + mediaTtl = 60.days ) } } @@ -426,7 +430,8 @@ private fun BackupAlertSheetContentPreviewGeneric() { private fun BackupAlertSheetContentPreviewPayment() { Previews.BottomSheetPreview { BackupAlertSheetContent( - backupAlert = BackupAlert.FailedToRenew + backupAlert = BackupAlert.FailedToRenew, + mediaTtl = 60.days ) } } @@ -437,7 +442,8 @@ private fun BackupAlertSheetContentPreviewMedia() { Previews.BottomSheetPreview { BackupAlertSheetContent( backupAlert = BackupAlert.MediaBackupsAreOff(endOfPeriodSeconds = System.currentTimeMillis().milliseconds.inWholeSeconds), - pricePerMonth = "$2.99" + pricePerMonth = "$2.99", + mediaTtl = 60.days ) } } @@ -447,7 +453,8 @@ private fun BackupAlertSheetContentPreviewMedia() { private fun BackupAlertSheetContentPreviewDelete() { Previews.BottomSheetPreview { BackupAlertSheetContent( - backupAlert = BackupAlert.MediaWillBeDeletedToday + backupAlert = BackupAlert.MediaWillBeDeletedToday, + mediaTtl = 60.days ) } } @@ -457,7 +464,8 @@ private fun BackupAlertSheetContentPreviewDelete() { private fun BackupAlertSheetContentPreviewDiskFull() { Previews.BottomSheetPreview { BackupAlertSheetContent( - backupAlert = BackupAlert.DiskFull(requiredSpace = "12GB") + backupAlert = BackupAlert.DiskFull(requiredSpace = "12GB"), + mediaTtl = 60.days ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsType.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsType.kt index 5083134fc8..4aa8af5190 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsType.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsType.kt @@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.backup.v2.ui.subscription import androidx.compose.runtime.Stable import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.backup.v2.MessageBackupTier +import kotlin.time.Duration /** * Represents a type of backup a user can select. @@ -19,7 +20,8 @@ sealed interface MessageBackupsType { data class Paid( val pricePerMonth: FiatMoney, - val storageAllowanceBytes: Long + val storageAllowanceBytes: Long, + val mediaTtl: Duration ) : MessageBackupsType { override val tier: MessageBackupTier = MessageBackupTier.PAID } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt index 4d03d847b5..312cd72a07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsTypeSelectionScreen.kt @@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.ByteUnit import java.math.BigDecimal import java.util.Currency +import kotlin.time.Duration.Companion.days import org.signal.core.ui.R as CoreUiR /** @@ -369,7 +370,8 @@ fun testBackupTypes(): List { ), MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.ONE, Currency.getInstance("USD")), - storageAllowanceBytes = 107374182400 + storageAllowanceBytes = 107374182400, + mediaTtl = 30.days ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt index 73f84115d4..a157b2f534 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsFragment.kt @@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate import java.math.BigDecimal import java.util.Currency import java.util.Locale +import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.seconds import org.signal.core.ui.R as CoreUiR @@ -342,7 +343,8 @@ private fun BackupsSettingsContentPreview() { enabledState = BackupsSettingsState.EnabledState.Active( type = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(4), Currency.getInstance("CAD")), - storageAllowanceBytes = 1_000_000 + storageAllowanceBytes = 1_000_000, + mediaTtl = 30.days ), expiresAt = 0.seconds, lastBackupAt = 0.seconds @@ -388,7 +390,8 @@ private fun ActivePaidBackupsRowPreview() { enabledState = BackupsSettingsState.EnabledState.Active( type = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(4), Currency.getInstance("CAD")), - storageAllowanceBytes = 1_000_000 + storageAllowanceBytes = 1_000_000, + mediaTtl = 30.days ), expiresAt = 0.seconds, lastBackupAt = 0.seconds diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt index ecde2a4fb2..e56eb3ae75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/BackupsSettingsViewModel.kt @@ -101,7 +101,8 @@ class BackupsSettingsViewModel : ViewModel() { activeSubscription.activeSubscription.amount, Currency.getInstance(activeSubscription.activeSubscription.currency) ), - storageAllowanceBytes = backupType.storageAllowanceBytes + storageAllowanceBytes = backupType.storageAllowanceBytes, + mediaTtl = backupType.mediaTtl ) ) } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index 45c90479ca..671dc83bb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -1225,7 +1225,8 @@ private fun SubscriptionMismatchMissingGooglePlayCardPreview() { state = RemoteBackupsSettingsState.BackupState.SubscriptionMismatchMissingGooglePlay( messageBackupsType = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")), - storageAllowanceBytes = 100_000_000 + storageAllowanceBytes = 100_000_000, + mediaTtl = 30.days ), renewalTime = System.currentTimeMillis().milliseconds + 30.days ) @@ -1242,7 +1243,8 @@ private fun BackupCardPreview() { backupState = RemoteBackupsSettingsState.BackupState.ActivePaid( messageBackupsType = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")), - storageAllowanceBytes = 100_000_000 + storageAllowanceBytes = 100_000_000, + mediaTtl = 30.days ), renewalTime = 1727193018.seconds, price = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")) @@ -1253,7 +1255,8 @@ private fun BackupCardPreview() { backupState = RemoteBackupsSettingsState.BackupState.Canceled( messageBackupsType = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")), - storageAllowanceBytes = 100_000_000 + storageAllowanceBytes = 100_000_000, + mediaTtl = 30.days ), renewalTime = 1727193018.seconds ) @@ -1263,7 +1266,8 @@ private fun BackupCardPreview() { backupState = RemoteBackupsSettingsState.BackupState.Inactive( messageBackupsType = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")), - storageAllowanceBytes = 100_000_000 + storageAllowanceBytes = 100_000_000, + mediaTtl = 30.days ), renewalTime = 1727193018.seconds ) @@ -1273,7 +1277,8 @@ private fun BackupCardPreview() { backupState = RemoteBackupsSettingsState.BackupState.ActivePaid( messageBackupsType = MessageBackupsType.Paid( pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")), - storageAllowanceBytes = 100_000_000 + storageAllowanceBytes = 100_000_000, + mediaTtl = 30.days ), renewalTime = 1727193018.seconds, price = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("CAD")) diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java index 2ae3eceadc..a74741b048 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/SubscriptionsConfiguration.java @@ -108,6 +108,9 @@ public class SubscriptionsConfiguration { @JsonProperty("playProductId") private String playProductId; + @JsonProperty("mediaTtlDays") + private long mediaTtlDays; + public long getStorageAllowanceBytes() { return storageAllowanceBytes; } @@ -115,6 +118,10 @@ public class SubscriptionsConfiguration { public String getPlayProductId() { return playProductId; } + + public long getMediaTtlDays() { + return mediaTtlDays; + } } public Map getCurrencies() {