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 f416eb1691..2320fcef0b 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
@@ -169,7 +169,7 @@ private fun BackupsSettingsContent(
OtherWaysToBackUpHeading()
}
- is BackupState.ActiveFree, is BackupState.ActivePaid -> {
+ is BackupState.ActiveFree, is BackupState.ActivePaid, is BackupState.Canceled -> {
ActiveBackupsRow(
backupState = backupsSettingsState.backupState,
onBackupsRowClick = onBackupsRowClick,
@@ -210,15 +210,6 @@ private fun BackupsSettingsContent(
OtherWaysToBackUpHeading()
}
- is BackupState.Canceled -> {
- ActiveBackupsRow(
- backupState = backupsSettingsState.backupState,
- lastBackupAt = backupsSettingsState.lastBackupAt
- )
-
- OtherWaysToBackUpHeading()
- }
-
is BackupState.SubscriptionMismatchMissingGooglePlay -> {
ActiveBackupsRow(
backupState = backupsSettingsState.backupState,
@@ -421,13 +412,25 @@ private fun ActiveBackupsRow(
when (val type = backupState.messageBackupsType) {
is MessageBackupsType.Paid -> {
- Text(
- text = stringResource(
+ val body = if (backupState is BackupState.Canceled) {
+ stringResource(R.string.BackupsSettingsFragment__subscription_canceled)
+ } else {
+ stringResource(
R.string.BackupsSettingsFragment_s_month_renews_s,
FiatMoneyUtil.format(LocalContext.current.resources, type.pricePerMonth),
DateUtils.formatDateWithYear(Locale.getDefault(), backupState.renewalTime.inWholeMilliseconds)
- ),
- color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+
+ val color = if (backupState is BackupState.Canceled) {
+ MaterialTheme.colorScheme.error
+ } else {
+ MaterialTheme.colorScheme.onSurfaceVariant
+ }
+
+ Text(
+ text = body,
+ color = color,
style = MaterialTheme.typography.bodyMedium
)
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt
index 55555aff1e..df365aab51 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BackupSubscriptionCheckJob.kt
@@ -71,43 +71,43 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
override suspend fun doRun(): Result {
if (!SignalStore.account.isRegistered) {
- Log.i(TAG, "User is not registered. Clearing mismatch value and exiting.")
+ Log.i(TAG, "User is not registered. Clearing mismatch value and exiting.", true)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
}
if (!RemoteConfig.messageBackups) {
- Log.i(TAG, "Message backups feature is not available. Clearing mismatch value and exiting.")
+ Log.i(TAG, "Message backups feature is not available. Clearing mismatch value and exiting.", true)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
}
if (!AppDependencies.billingApi.isApiAvailable()) {
- Log.i(TAG, "Google Play Billing API is not available on this device. Clearing mismatch value and exiting.")
+ Log.i(TAG, "Google Play Billing API is not available on this device. Clearing mismatch value and exiting.", true)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
}
if (SignalStore.backup.deletionState != DeletionState.NONE) {
- Log.i(TAG, "User is in the process of or has delete their backup. Clearing mismatch value and exiting.")
+ Log.i(TAG, "User is in the process of or has delete their backup. Clearing mismatch value and exiting.", true)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
}
if (!SignalStore.backup.areBackupsEnabled) {
- Log.i(TAG, "Backups are not enabled on this device. Clearing mismatch value and exiting.")
+ Log.i(TAG, "Backups are not enabled on this device. Clearing mismatch value and exiting.", true)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
}
val purchase: BillingPurchaseResult = AppDependencies.billingApi.queryPurchases()
- Log.i(TAG, "Retrieved purchase result from Billing api: $purchase")
+ Log.i(TAG, "Retrieved purchase result from Billing api: $purchase", true)
val hasActivePurchase = purchase is BillingPurchaseResult.Success && purchase.isAcknowledged
val product: BillingProduct? = AppDependencies.billingApi.queryProduct()
if (product == null) {
- Log.w(TAG, "Google Play Billing product not available. Exiting.")
+ Log.w(TAG, "Google Play Billing product not available. Exiting.", true)
return Result.failure()
}
@@ -115,7 +115,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
val inAppPayment = SignalDatabase.inAppPayments.getLatestInAppPaymentByType(InAppPaymentType.RECURRING_BACKUP)
if (inAppPayment?.state == InAppPaymentTable.State.PENDING) {
- Log.i(TAG, "User has a pending in-app payment. Clearing mismatch value and re-checking later.")
+ Log.i(TAG, "User has a pending in-app payment. Clearing mismatch value and re-checking later.", true)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
}
@@ -125,9 +125,12 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
checkForFailedOrCanceledSubscriptionState(activeSubscription)
- Log.i(TAG, "Synchronizing backup tier with value from server.")
- BackupRepository.getBackupTier().runIfSuccessful {
- SignalStore.backup.backupTier = it
+ val isSignalSubscriptionFailedOrCanceled = activeSubscription?.isFailedPayment == true || activeSubscription?.isCanceled == true
+ if (hasActiveSignalSubscription && !isSignalSubscriptionFailedOrCanceled) {
+ Log.i(TAG, "Detected an active, non-failed, non-canceled signal subscription. Synchronizing backup tier with value from server.", true)
+ BackupRepository.getBackupTier().runIfSuccessful {
+ SignalStore.backup.backupTier = it
+ }
}
val hasActivePaidBackupTier = SignalStore.backup.backupTier == MessageBackupTier.PAID
@@ -135,14 +138,14 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
val hasValidInactiveState = !hasActivePaidBackupTier && !hasActiveSignalSubscription && !hasActivePurchase
val purchaseToken = if (hasActivePurchase) {
- (purchase as BillingPurchaseResult.Success).purchaseToken
+ purchase.purchaseToken
} else {
null
}
val hasTokenMismatch = purchaseToken?.let { hasLocalDevicePurchaseTokenMismatch(purchaseToken) } == true
if (hasActiveSignalSubscription && hasTokenMismatch) {
- Log.i(TAG, "Encountered token mismatch with an active Signal subscription. Attempting to redeem against latest token.")
+ Log.i(TAG, "Encountered token mismatch with an active Signal subscription. Attempting to redeem against latest token.", true)
enqueueRedemptionForNewToken(purchaseToken, product.price)
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
@@ -152,9 +155,17 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
SignalStore.backup.subscriptionStateMismatchDetected = false
return Result.success()
} else {
- Log.w(TAG, "State mismatch: (hasActivePaidBackupTier: $hasActivePaidBackupTier, hasActiveSignalSubscription: $hasActiveSignalSubscription, hasActivePurchase: $hasActivePurchase). Setting mismatch value and exiting.", true)
- SignalStore.backup.subscriptionStateMismatchDetected = true
- return Result.success()
+ val isGooglePlayBillingCanceled = purchase is BillingPurchaseResult.Success && !purchase.isAutoRenewing
+
+ if (isGooglePlayBillingCanceled && (!hasActiveSignalSubscription || isSignalSubscriptionFailedOrCanceled)) {
+ Log.i(TAG, "Valid cancel state. Clearing mismatch. (isGooglePlayBillingCanceled: true, hasActiveSignalSubscription: $hasActiveSignalSubscription, isSignalSubscriptionFailedOrCanceled: $isSignalSubscriptionFailedOrCanceled", true)
+ SignalStore.backup.subscriptionStateMismatchDetected = false
+ return Result.success()
+ } else {
+ Log.w(TAG, "State mismatch: (hasActivePaidBackupTier: $hasActivePaidBackupTier, hasActiveSignalSubscription: $hasActiveSignalSubscription, hasActivePurchase: $hasActivePurchase). Setting mismatch value and exiting.", true)
+ SignalStore.backup.subscriptionStateMismatchDetected = true
+ return Result.success()
+ }
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6d57c8d141..6327e55b2a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8072,6 +8072,8 @@
%1$s/month, renews %2$s
Last backup %1$s
+
+ Subscription canceled
Automatic backups with Signal\'s secure end-to-end encrypted storage service.