mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-08 09:18:39 +01:00
Add "your media will be deleted today" mechanism based off last checkin time and media TTL.
This commit is contained in:
committed by
Greyson Parrelli
parent
f16827d9ec
commit
bae86d127f
@@ -101,6 +101,7 @@ import java.time.ZonedDateTime
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
import kotlin.time.Duration.Companion.hours
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import org.signal.libsignal.messagebackup.MessageBackupKey as LibSignalMessageBackupKey
|
import org.signal.libsignal.messagebackup.MessageBackupKey as LibSignalMessageBackupKey
|
||||||
|
|
||||||
@@ -237,6 +238,49 @@ object BackupRepository {
|
|||||||
return System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime
|
return System.currentTimeMillis().milliseconds > SignalStore.backup.nextBackupFailureSheetSnoozeTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun snoozeYourMediaWillBeDeletedTodaySheet() {
|
||||||
|
SignalStore.backup.lastCheckInSnoozeMillis = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the "Your media will be deleted today" sheet should be displayed.
|
||||||
|
*/
|
||||||
|
suspend fun shouldDisplayYourMediaWillBeDeletedTodaySheet(): Boolean {
|
||||||
|
if (shouldNotDisplayBackupFailedMessaging() || !SignalStore.backup.optimizeStorage) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val paidType = try {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
getPaidType()
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.w(TAG, "Failed to retrieve paid type.", e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paidType == null) {
|
||||||
|
Log.w(TAG, "Paid type is not available on this device.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastCheckIn = SignalStore.backup.lastCheckInMillis.milliseconds
|
||||||
|
if (lastCheckIn == 0.milliseconds) {
|
||||||
|
Log.w(TAG, "LastCheckIn has not yet been set.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastSnoozeTime = SignalStore.backup.lastCheckInSnoozeMillis.milliseconds
|
||||||
|
val now = System.currentTimeMillis().milliseconds
|
||||||
|
val mediaTtl = paidType.mediaTtl
|
||||||
|
val mediaExpiration = lastCheckIn + mediaTtl
|
||||||
|
|
||||||
|
val isNowAfterSnooze = now < lastSnoozeTime || now >= lastSnoozeTime + 4.hours
|
||||||
|
val isNowWithin24HoursOfMediaExpiration = now < mediaExpiration && (mediaExpiration - now) <= 1.days
|
||||||
|
|
||||||
|
return isNowAfterSnooze && isNowWithin24HoursOfMediaExpiration
|
||||||
|
}
|
||||||
|
|
||||||
private fun shouldNotDisplayBackupFailedMessaging(): Boolean {
|
private fun shouldNotDisplayBackupFailedMessaging(): Boolean {
|
||||||
return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled || !SignalStore.backup.hasBackupBeenUploaded
|
return !RemoteConfig.messageBackups || !SignalStore.backup.areBackupsEnabled || !SignalStore.backup.hasBackupBeenUploaded
|
||||||
}
|
}
|
||||||
@@ -1178,7 +1222,7 @@ object BackupRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getFreeType(): MessageBackupsType {
|
private suspend fun getFreeType(): MessageBackupsType.Free {
|
||||||
val config = getSubscriptionsConfiguration()
|
val config = getSubscriptionsConfiguration()
|
||||||
|
|
||||||
return MessageBackupsType.Free(
|
return MessageBackupsType.Free(
|
||||||
@@ -1186,7 +1230,7 @@ object BackupRepository {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getPaidType(): MessageBackupsType? {
|
private suspend fun getPaidType(): MessageBackupsType.Paid? {
|
||||||
val config = getSubscriptionsConfiguration()
|
val config = getSubscriptionsConfiguration()
|
||||||
val product = AppDependencies.billingApi.queryProduct() ?: return null
|
val product = AppDependencies.billingApi.queryProduct() ?: return null
|
||||||
val backupLevelConfiguration = config.backupConfiguration.backupLevelConfigurationMap[SubscriptionsConfiguration.BACKUPS_LEVEL] ?: return null
|
val backupLevelConfiguration = config.backupConfiguration.backupLevelConfigurationMap[SubscriptionsConfiguration.BACKUPS_LEVEL] ?: return null
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class BackupAlertBottomSheet : UpgradeToPaidTierBottomSheet() {
|
|||||||
|
|
||||||
when (backupAlert) {
|
when (backupAlert) {
|
||||||
is BackupAlert.CouldNotCompleteBackup -> BackupRepository.markBackupFailedSheetDismissed()
|
is BackupAlert.CouldNotCompleteBackup -> BackupRepository.markBackupFailedSheetDismissed()
|
||||||
|
is BackupAlert.MediaWillBeDeletedToday -> BackupRepository.snoozeYourMediaWillBeDeletedTodaySheet()
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,9 @@ object BackupAlertDelegate {
|
|||||||
BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = SignalStore.backup.daysSinceLastBackup)).show(fragmentManager, null)
|
BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup(daysSinceLastBackup = SignalStore.backup.daysSinceLastBackup)).show(fragmentManager, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO [backups] Check if media will be deleted within 24hrs and display warning sheet.
|
if (BackupRepository.shouldDisplayYourMediaWillBeDeletedTodaySheet()) {
|
||||||
|
BackupAlertBottomSheet.create(BackupAlert.MediaWillBeDeletedToday)
|
||||||
// TODO [backups]
|
}
|
||||||
// Get unnotified backup download failures & display sheet
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-27
@@ -59,7 +59,6 @@ import java.util.concurrent.locks.Lock
|
|||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -390,32 +389,6 @@ 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
|
@JvmStatic
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun setShouldCancelSubscriptionBeforeNextSubscribeAttempt(subscriber: InAppPaymentSubscriberRecord, shouldCancel: Boolean) {
|
fun setShouldCancelSubscriptionBeforeNextSubscribeAttempt(subscriber: InAppPaymentSubscriberRecord, shouldCancel: Boolean) {
|
||||||
|
|||||||
-11
@@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.badges.Badges
|
|||||||
import org.thoughtcrime.securesms.badges.self.expired.MonthlyDonationCanceledBottomSheetDialogFragment
|
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.DonationPendingBottomSheet
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPendingBottomSheetArgs
|
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.ThanksForYourSupportBottomSheetDialogFragment
|
||||||
import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragmentArgs
|
import org.thoughtcrime.securesms.components.settings.app.subscription.thanks.ThanksForYourSupportBottomSheetDialogFragmentArgs
|
||||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||||
@@ -140,16 +139,6 @@ class InAppPaymentsBottomSheetDelegate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.MediaWillBeDeletedToday).show(fragmentManager, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isUnexpectedCancellation(inAppPaymentState: InAppPaymentTable.State, inAppPaymentData: InAppPaymentData): Boolean {
|
private fun isUnexpectedCancellation(inAppPaymentState: InAppPaymentTable.State, inAppPaymentData: InAppPaymentData): Boolean {
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class BackupRefreshJob private constructor(
|
|||||||
return when (result) {
|
return when (result) {
|
||||||
is NetworkResult.Success -> {
|
is NetworkResult.Success -> {
|
||||||
SignalStore.backup.lastCheckInMillis = System.currentTimeMillis()
|
SignalStore.backup.lastCheckInMillis = System.currentTimeMillis()
|
||||||
|
SignalStore.backup.lastCheckInSnoozeMillis = 0
|
||||||
Result.success()
|
Result.success()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ class InAppPaymentRedemptionJob private constructor(
|
|||||||
Log.i(TAG, "Setting backup tier to PAID", true)
|
Log.i(TAG, "Setting backup tier to PAID", true)
|
||||||
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
||||||
SignalStore.backup.lastCheckInMillis = System.currentTimeMillis()
|
SignalStore.backup.lastCheckInMillis = System.currentTimeMillis()
|
||||||
|
SignalStore.backup.lastCheckInSnoozeMillis = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
|
|||||||
private const val KEY_BACKUP_TIER = "backup.backupTier"
|
private const val KEY_BACKUP_TIER = "backup.backupTier"
|
||||||
private const val KEY_LATEST_BACKUP_TIER = "backup.latestBackupTier"
|
private const val KEY_LATEST_BACKUP_TIER = "backup.latestBackupTier"
|
||||||
private const val KEY_LAST_CHECK_IN_MILLIS = "backup.lastCheckInMilliseconds"
|
private const val KEY_LAST_CHECK_IN_MILLIS = "backup.lastCheckInMilliseconds"
|
||||||
|
private const val KEY_LAST_CHECK_IN_SNOOZE_MILLIS = "backup.lastCheckInSnoozeMilliseconds"
|
||||||
|
|
||||||
private const val KEY_NEXT_BACKUP_TIME = "backup.nextBackupTime"
|
private const val KEY_NEXT_BACKUP_TIME = "backup.nextBackupTime"
|
||||||
private const val KEY_LAST_BACKUP_TIME = "backup.lastBackupTime"
|
private const val KEY_LAST_BACKUP_TIME = "backup.lastBackupTime"
|
||||||
@@ -95,8 +96,19 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
|
|||||||
|
|
||||||
var userManuallySkippedMediaRestore: Boolean by booleanValue(KEY_USER_MANUALLY_SKIPPED_MEDIA_RESTORE, false)
|
var userManuallySkippedMediaRestore: Boolean by booleanValue(KEY_USER_MANUALLY_SKIPPED_MEDIA_RESTORE, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last time the device notified the server that the archive is still in use.
|
||||||
|
*/
|
||||||
var lastCheckInMillis: Long by longValue(KEY_LAST_CHECK_IN_MILLIS, 0L)
|
var lastCheckInMillis: Long by longValue(KEY_LAST_CHECK_IN_MILLIS, 0L)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time we last displayed the "Your media will be deleted today" sheet.
|
||||||
|
*
|
||||||
|
* Set when the user dismisses the "Your media will be deleted today" alert
|
||||||
|
* Cleared when the system performs a check-in or the user subscribes to backups.
|
||||||
|
*/
|
||||||
|
var lastCheckInSnoozeMillis: Long by longValue(KEY_LAST_CHECK_IN_SNOOZE_MILLIS, 0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key used to backup messages.
|
* Key used to backup messages.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
|
|||||||
private const val SUBSCRIPTION_CANCELATION_TIMESTAMP = "donation.subscription.cancelation.timestamp"
|
private const val SUBSCRIPTION_CANCELATION_TIMESTAMP = "donation.subscription.cancelation.timestamp"
|
||||||
private const val SUBSCRIPTION_CANCELATION_WATERMARK = "donation.subscription.cancelation.watermark"
|
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_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
|
* The current request context for subscription. This should be stored until either
|
||||||
@@ -162,8 +161,7 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
|
|||||||
SUBSCRIPTION_EOP_STARTED_TO_CONVERT,
|
SUBSCRIPTION_EOP_STARTED_TO_CONVERT,
|
||||||
SUBSCRIPTION_EOP_STARTED_TO_REDEEM,
|
SUBSCRIPTION_EOP_STARTED_TO_REDEEM,
|
||||||
SUBSCRIPTION_EOP_REDEEMED,
|
SUBSCRIPTION_EOP_REDEEMED,
|
||||||
SUBSCRIPTION_PAYMENT_SOURCE_TYPE,
|
SUBSCRIPTION_PAYMENT_SOURCE_TYPE
|
||||||
SHOW_LAST_DAY_TO_DOWNLOAD_MEDIA_DIALOG
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val recurringDonationCurrencyPublisher: Subject<Currency> by lazy { BehaviorSubject.createDefault(getSubscriptionCurrency(InAppPaymentSubscriberRecord.Type.DONATION)) }
|
private val recurringDonationCurrencyPublisher: Subject<Currency> by lazy { BehaviorSubject.createDefault(getSubscriptionCurrency(InAppPaymentSubscriberRecord.Type.DONATION)) }
|
||||||
@@ -420,9 +418,6 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
|
|||||||
@get:JvmName("showCantProcessDialog")
|
@get:JvmName("showCantProcessDialog")
|
||||||
var showMonthlyDonationCanceledDialog: Boolean by booleanValue(SHOW_CANT_PROCESS_DIALOG, true)
|
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
|
* Denotes that the previous attempt to subscribe failed in some way. Either an
|
||||||
* automatic renewal failed resulting in an unexpected expiration, or payment failed
|
* automatic renewal failed resulting in an unexpected expiration, or payment failed
|
||||||
|
|||||||
Reference in New Issue
Block a user