Add new dialog and sheet for handling offloaded media after a subscription is canceled or expires.

This commit is contained in:
Alex Hart
2025-06-09 15:41:10 -03:00
committed by Greyson Parrelli
parent 18b5354944
commit 1424dd6892
11 changed files with 562 additions and 68 deletions

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.keyvalue
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.keyvalue.BackupValues.Companion.TAG
import org.thoughtcrime.securesms.keyvalue.protos.BackupDownloadNotifierState
import kotlin.math.max
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
/**
* Manages setting and snoozing notifiers informing the user to download their backup
* before it is deleted from the Signal service.
*
* This is only meant to be delegated to from [BackupValues]
*/
object BackupDownloadNotifierUtil {
/**
* Sets the notifier to trigger half way between now and the entitlement expiration time.
*
* @param state The current state, or null.
* @param entitlementExpirationTime The time the user's backup entitlement expires
* @param now The current time, for testing.
*
* @return the new state value.
*/
fun setDownloadNotifierToTriggerAtHalfwayPoint(
state: BackupDownloadNotifierState?,
entitlementExpirationTime: Duration,
now: Duration = System.currentTimeMillis().milliseconds
): BackupDownloadNotifierState? {
if (state?.entitlementExpirationSeconds == entitlementExpirationTime.inWholeSeconds) {
Log.d(TAG, "Entitlement expiration time already present.")
return state
}
if (now >= entitlementExpirationTime) {
Log.i(TAG, "Entitlement expiration time is in the past. Clearing state.")
return null
}
val timeRemaining = entitlementExpirationTime - now
val halfWayPoint = (entitlementExpirationTime - timeRemaining / 2)
val lastDay = entitlementExpirationTime - 1.days
val nextIntervalSeconds: Duration = when {
timeRemaining <= 1.days -> 0.seconds
timeRemaining <= 4.days -> lastDay - now
else -> halfWayPoint - now
}
return BackupDownloadNotifierState(
entitlementExpirationSeconds = entitlementExpirationTime.inWholeSeconds,
lastSheetDisplaySeconds = now.inWholeSeconds,
intervalSeconds = nextIntervalSeconds.inWholeSeconds,
type = BackupDownloadNotifierState.Type.SHEET
)
}
/**
* Sets the notifier to trigger either one day before or four hours before expiration.
*
* @param state The current state, or null.
* @param now The current time, for testing.
*
* @return The new state value.
*/
fun snoozeDownloadNotifier(
state: BackupDownloadNotifierState?,
now: Duration = System.currentTimeMillis().milliseconds
): BackupDownloadNotifierState? {
state ?: return null
if (state.type == BackupDownloadNotifierState.Type.DIALOG) {
Log.i(TAG, "Clearing state after dismissing download notifier dialog.")
return null
}
val lastDay = state.entitlementExpirationSeconds.seconds - 1.days
return if (now >= lastDay) {
val fourHoursPriorToExpiration = state.entitlementExpirationSeconds.seconds - 4.hours
state.newBuilder()
.lastSheetDisplaySeconds(now.inWholeSeconds)
.intervalSeconds(max(0L, (fourHoursPriorToExpiration - now).inWholeSeconds))
.type(BackupDownloadNotifierState.Type.DIALOG)
.build()
} else {
val timeUntilLastDay = lastDay - now
state.newBuilder()
.lastSheetDisplaySeconds(now.inWholeSeconds)
.intervalSeconds(timeUntilLastDay.inWholeSeconds)
.type(BackupDownloadNotifierState.Type.SHEET)
.build()
}
}
}

View File

@@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.jobmanager.impl.NoRemoteArchiveGarbageCollectionPendingConstraint
import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraintObserver
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
import org.thoughtcrime.securesms.keyvalue.protos.BackupDownloadNotifierState
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential
@@ -52,6 +53,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
private const val KEY_CDN_MEDIA_PATH = "backup.cdn.mediaPath"
private const val KEY_BACKUP_DOWNLOAD_NOTIFIER_STATE = "backup.downloadNotifierState"
private const val KEY_BACKUP_OVER_CELLULAR = "backup.useCellular"
private const val KEY_RESTORE_OVER_CELLULAR = "backup.restore.useCellular"
private const val KEY_OPTIMIZE_STORAGE = "backup.optimizeStorage"
@@ -97,6 +99,9 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
var optimizeStorage: Boolean by booleanValue(KEY_OPTIMIZE_STORAGE, false)
var backupWithCellular: Boolean by booleanValue(KEY_BACKUP_OVER_CELLULAR, false)
var backupDownloadNotifierState: BackupDownloadNotifierState? by protoValue(KEY_BACKUP_DOWNLOAD_NOTIFIER_STATE, BackupDownloadNotifierState.ADAPTER)
private set
var restoreWithCellular: Boolean
get() = getBoolean(KEY_RESTORE_OVER_CELLULAR, false)
set(value) {
@@ -252,6 +257,28 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
*/
var spaceAvailableOnDiskBytes: Long by longValue(KEY_BACKUP_FAIL_SPACE_REMAINING, -1L)
/**
* Sets the notifier to trigger half way between now and the entitlement expiration time.
*/
fun setDownloadNotifierToTriggerAtHalfwayPoint(entitlementExpirationTime: Duration) {
backupDownloadNotifierState = BackupDownloadNotifierUtil.setDownloadNotifierToTriggerAtHalfwayPoint(backupDownloadNotifierState, entitlementExpirationTime)
}
/**
* Sets the notifier to trigger 24hrs before the end of the grace period.
*
*/
fun snoozeDownloadNotifier() {
backupDownloadNotifierState = BackupDownloadNotifierUtil.snoozeDownloadNotifier(backupDownloadNotifierState)
}
/**
* Clears the notifier state, done when the user subscribes to the paid tier.
*/
fun clearDownloadNotifierState() {
backupDownloadNotifierState = null
}
fun internalSetBackupFailedErrorState() {
markMessageBackupFailure()
putLong(KEY_BACKUP_FAIL_SHEET_SNOOZE_TIME, 0)