mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-21 03:28:47 +00:00
Improve free tier UX around media.
This commit is contained in:
committed by
Jeffrey Starke
parent
c5753b96ff
commit
cbfdc4b57a
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
@@ -29,6 +30,7 @@ import org.signal.core.ui.compose.Buttons
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.SignalPreview
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.app.backups.BackupStateObserver
|
||||
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.jobs.BackupMessagesJob
|
||||
import org.signal.core.ui.R as CoreUiR
|
||||
@@ -51,15 +53,15 @@ class CreateBackupBottomSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val isPaidTier: Boolean = remember { BackupStateObserver.getNonIOBackupState().isLikelyPaidTier() }
|
||||
|
||||
CreateBackupBottomSheetContent(
|
||||
isPaidTier = isPaidTier,
|
||||
onBackupNowClick = {
|
||||
BackupMessagesJob.enqueue()
|
||||
setFragmentResult(REQUEST_KEY, bundleOf(REQUEST_KEY to Result.BACKUP_STARTED))
|
||||
isResultSet = true
|
||||
dismissAllowingStateLoss()
|
||||
},
|
||||
onBackupLaterClick = {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -80,8 +82,8 @@ class CreateBackupBottomSheet : ComposeBottomSheetDialogFragment() {
|
||||
|
||||
@Composable
|
||||
private fun CreateBackupBottomSheetContent(
|
||||
onBackupNowClick: () -> Unit,
|
||||
onBackupLaterClick: () -> Unit
|
||||
isPaidTier: Boolean,
|
||||
onBackupNowClick: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -106,8 +108,14 @@ private fun CreateBackupBottomSheetContent(
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
val body = if (isPaidTier) {
|
||||
stringResource(id = R.string.CreateBackupBottomSheet__depending_on_the_size)
|
||||
} else {
|
||||
stringResource(id = R.string.CreateBackupBottomSheet__free_tier)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.CreateBackupBottomSheet__depending_on_the_size),
|
||||
text = body,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
@@ -128,11 +136,22 @@ private fun CreateBackupBottomSheetContent(
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun CreateBackupBottomSheetContentPreview() {
|
||||
private fun CreateBackupBottomSheetContentPaidPreview() {
|
||||
Previews.BottomSheetPreview {
|
||||
CreateBackupBottomSheetContent(
|
||||
onBackupNowClick = {},
|
||||
onBackupLaterClick = {}
|
||||
isPaidTier = true,
|
||||
onBackupNowClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun CreateBackupBottomSheetContentFreePreview() {
|
||||
Previews.BottomSheetPreview {
|
||||
CreateBackupBottomSheetContent(
|
||||
isPaidTier = false,
|
||||
onBackupNowClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ import org.thoughtcrime.securesms.help.HelpFragment
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
@@ -285,6 +286,14 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
override fun onIncludeDebuglogClick(newState: Boolean) {
|
||||
viewModel.setIncludeDebuglog(newState)
|
||||
}
|
||||
|
||||
override fun onMediaBackupSizeClick() {
|
||||
viewModel.requestDialog(RemoteBackupsSettingsState.Dialog.FREE_TIER_MEDIA_EXPLAINER)
|
||||
}
|
||||
|
||||
override fun onFreeTierBackupSizeLearnMore() {
|
||||
CommunicationActions.openBrowserLink(requireContext(), "https://support.signal.org/hc/articles/9708267671322")
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayBackupKey() {
|
||||
@@ -387,6 +396,8 @@ private interface ContentCallbacks {
|
||||
fun onDisplayDownloadingBackupDialog() = Unit
|
||||
fun onManageStorageClick() = Unit
|
||||
fun onIncludeDebuglogClick(newState: Boolean) = Unit
|
||||
fun onMediaBackupSizeClick() = Unit
|
||||
fun onFreeTierBackupSizeLearnMore() = Unit
|
||||
|
||||
object Empty : ContentCallbacks
|
||||
}
|
||||
@@ -634,6 +645,18 @@ private fun RemoteBackupsSettingsContent(
|
||||
onResumeOverCellularClick = contentCallbacks::onRestoreUsingCellularClick
|
||||
)
|
||||
}
|
||||
|
||||
RemoteBackupsSettingsState.Dialog.FREE_TIER_MEDIA_EXPLAINER -> {
|
||||
Dialogs.SimpleAlertDialog(
|
||||
title = stringResource(R.string.RemoteBackupsSettingsFragment__free_tier_storage_title),
|
||||
body = pluralStringResource(R.plurals.RemoteBackupsSettingsFragment__backup_frequency_dialog_body, state.freeTierMediaRetentionDays, state.freeTierMediaRetentionDays),
|
||||
confirm = stringResource(android.R.string.ok),
|
||||
dismiss = stringResource(R.string.RemoteBackupsSettingsFragment__learn_more),
|
||||
onConfirm = {},
|
||||
onDismiss = contentCallbacks::onDialogDismissed,
|
||||
onDeny = contentCallbacks::onFreeTierBackupSizeLearnMore
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val snackbarMessageId = remember(state.snackbar) {
|
||||
@@ -902,6 +925,7 @@ private fun LazyListScope.appendBackupDetailsItems(
|
||||
item {
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = backupProgress,
|
||||
isPaidTier = state.tier == MessageBackupTier.PAID,
|
||||
canBackupMessagesRun = state.canBackupMessagesJobRun,
|
||||
canBackupUsingCellular = state.canBackUpUsingCellular,
|
||||
cancelArchiveUpload = contentCallbacks::onCancelUploadClick
|
||||
@@ -909,7 +933,6 @@ private fun LazyListScope.appendBackupDetailsItems(
|
||||
}
|
||||
}
|
||||
|
||||
if (state.backupState.isLikelyPaidTier()) {
|
||||
item {
|
||||
val sizeText = if (state.backupMediaSize < 0L) {
|
||||
stringResource(R.string.RemoteBackupsSettingsFragment__calculating)
|
||||
@@ -917,7 +940,8 @@ private fun LazyListScope.appendBackupDetailsItems(
|
||||
state.backupMediaSize.bytes.toUnitString()
|
||||
}
|
||||
|
||||
Rows.TextRow(text = {
|
||||
Rows.TextRow(
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_size),
|
||||
@@ -930,8 +954,13 @@ private fun LazyListScope.appendBackupDetailsItems(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
onClick = if (state.backupMediaSize >= 0L && state.tier == MessageBackupTier.FREE) {
|
||||
{ contentCallbacks.onMediaBackupSizeClick() }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -1353,6 +1382,7 @@ private fun SubscriptionMismatchMissingGooglePlayCard(
|
||||
@Composable
|
||||
private fun InProgressBackupRow(
|
||||
archiveUploadProgressState: ArchiveUploadProgressState,
|
||||
isPaidTier: Boolean,
|
||||
canBackupMessagesRun: Boolean = true,
|
||||
canBackupUsingCellular: Boolean = true,
|
||||
cancelArchiveUpload: () -> Unit = {}
|
||||
@@ -1390,7 +1420,7 @@ private fun InProgressBackupRow(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = getProgressStateMessage(archiveUploadProgressState, canBackupMessagesRun, canBackupUsingCellular),
|
||||
text = getProgressStateMessage(archiveUploadProgressState, isPaidTier, canBackupMessagesRun, canBackupUsingCellular),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
@@ -1428,11 +1458,11 @@ private fun ArchiveProgressIndicator(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getProgressStateMessage(archiveUploadProgressState: ArchiveUploadProgressState, canBackupMessagesRun: Boolean, canBackupUsingCellular: Boolean): String {
|
||||
private fun getProgressStateMessage(archiveUploadProgressState: ArchiveUploadProgressState, isPaidTier: Boolean, canBackupMessagesRun: Boolean, canBackupUsingCellular: Boolean): String {
|
||||
return when (archiveUploadProgressState.state) {
|
||||
ArchiveUploadProgressState.State.None, ArchiveUploadProgressState.State.UserCanceled -> stringResource(R.string.RemoteBackupsSettingsFragment__processing_backup)
|
||||
ArchiveUploadProgressState.State.Export -> getBackupExportPhaseProgressString(archiveUploadProgressState, canBackupMessagesRun, canBackupUsingCellular)
|
||||
ArchiveUploadProgressState.State.UploadBackupFile, ArchiveUploadProgressState.State.UploadMedia -> getBackupUploadPhaseProgressString(archiveUploadProgressState)
|
||||
ArchiveUploadProgressState.State.UploadBackupFile, ArchiveUploadProgressState.State.UploadMedia -> getBackupUploadPhaseProgressString(archiveUploadProgressState, isPaidTier)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1464,12 +1494,16 @@ private fun getBackupExportPhaseProgressString(state: ArchiveUploadProgressState
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getBackupUploadPhaseProgressString(state: ArchiveUploadProgressState): String {
|
||||
private fun getBackupUploadPhaseProgressString(state: ArchiveUploadProgressState, isPaidTier: Boolean): String {
|
||||
val formattedTotalBytes = state.uploadBytesTotal.bytes.toUnitString()
|
||||
val formattedUploadedBytes = state.uploadBytesUploaded.bytes.toUnitString()
|
||||
val percent = (state.uploadProgress() * 100).toInt()
|
||||
|
||||
return stringResource(R.string.RemoteBackupsSettingsFragment__uploading_s_of_s_d, formattedUploadedBytes, formattedTotalBytes, percent)
|
||||
return if (isPaidTier) {
|
||||
stringResource(R.string.RemoteBackupsSettingsFragment__uploading_s_of_s_d, formattedUploadedBytes, formattedTotalBytes, percent)
|
||||
} else {
|
||||
stringResource(R.string.RemoteBackupsSettingsFragment__uploading_d, percent)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1949,18 +1983,20 @@ private fun LastBackupRowPreview() {
|
||||
private fun InProgressRowPreview() {
|
||||
Previews.Preview {
|
||||
Column {
|
||||
InProgressBackupRow(archiveUploadProgressState = ArchiveUploadProgressState())
|
||||
InProgressBackupRow(archiveUploadProgressState = ArchiveUploadProgressState(), isPaidTier = true)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.BackupPhaseNone
|
||||
)
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.Export,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Account
|
||||
)
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
@@ -1968,7 +2004,8 @@ private fun InProgressRowPreview() {
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Message,
|
||||
frameExportCount = 1,
|
||||
frameTotalCount = 1
|
||||
)
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
@@ -1976,7 +2013,8 @@ private fun InProgressRowPreview() {
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Message,
|
||||
frameExportCount = 1000,
|
||||
frameTotalCount = 100_000
|
||||
)
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
@@ -1984,7 +2022,8 @@ private fun InProgressRowPreview() {
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.Message,
|
||||
frameExportCount = 1_000_000,
|
||||
frameTotalCount = 100_000
|
||||
)
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
@@ -1994,7 +2033,19 @@ private fun InProgressRowPreview() {
|
||||
backupFileTotalBytes = 50.mebiBytes.inWholeBytes,
|
||||
mediaUploadedBytes = 0,
|
||||
mediaTotalBytes = 0
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
state = ArchiveUploadProgressState.State.UploadBackupFile,
|
||||
backupPhase = ArchiveUploadProgressState.BackupPhase.BackupPhaseNone,
|
||||
backupFileUploadedBytes = 10.mebiBytes.inWholeBytes,
|
||||
backupFileTotalBytes = 50.mebiBytes.inWholeBytes,
|
||||
mediaUploadedBytes = 0,
|
||||
mediaTotalBytes = 0
|
||||
),
|
||||
isPaidTier = false
|
||||
)
|
||||
InProgressBackupRow(
|
||||
archiveUploadProgressState = ArchiveUploadProgressState(
|
||||
@@ -2004,7 +2055,8 @@ private fun InProgressRowPreview() {
|
||||
backupFileTotalBytes = 50.mebiBytes.inWholeBytes,
|
||||
mediaUploadedBytes = 100.mebiBytes.inWholeBytes,
|
||||
mediaTotalBytes = 1.gibiBytes.inWholeBytes
|
||||
)
|
||||
),
|
||||
isPaidTier = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ data class RemoteBackupsSettingsState(
|
||||
val canBackupMessagesJobRun: Boolean = false,
|
||||
val backupMediaDetails: BackupMediaDetails? = null,
|
||||
val showBackupCreateFailedError: Boolean = false,
|
||||
val showBackupCreateCouldNotCompleteError: Boolean = false
|
||||
val showBackupCreateCouldNotCompleteError: Boolean = false,
|
||||
val freeTierMediaRetentionDays: Int = -1
|
||||
) {
|
||||
|
||||
data class BackupMediaDetails(
|
||||
@@ -50,7 +51,8 @@ data class RemoteBackupsSettingsState(
|
||||
SUBSCRIPTION_NOT_FOUND,
|
||||
SKIP_MEDIA_RESTORE_PROTECTION,
|
||||
CANCEL_MEDIA_RESTORE_PROTECTION,
|
||||
RESTORE_OVER_CELLULAR_PROTECTION
|
||||
RESTORE_OVER_CELLULAR_PROTECTION,
|
||||
FREE_TIER_MEDIA_EXPLAINER
|
||||
}
|
||||
|
||||
enum class Snackbar {
|
||||
|
||||
@@ -33,6 +33,9 @@ import org.thoughtcrime.securesms.backup.DeletionState
|
||||
import org.thoughtcrime.securesms.backup.v2.ArchiveRestoreProgress
|
||||
import org.thoughtcrime.securesms.backup.v2.ArchiveRestoreProgressState.RestoreStatus
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.components.settings.app.backups.BackupState
|
||||
import org.thoughtcrime.securesms.components.settings.app.backups.BackupStateObserver
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
@@ -47,6 +50,8 @@ import org.thoughtcrime.securesms.util.Environment
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.api.NetworkResult
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
@@ -163,11 +168,12 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
BackupStateObserver(viewModelScope).backupState.collect { state ->
|
||||
_state.update {
|
||||
it.copy(backupState = state)
|
||||
}
|
||||
refreshState(null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,8 +264,10 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
|
||||
private fun refreshBackupMediaSizeState() {
|
||||
_state.update {
|
||||
val (mediaSize, mediaRetentionDays) = getBackupMediaSize(it.tier, (it.backupState as? BackupState.WithTypeAndRenewalTime)?.messageBackupsType)
|
||||
it.copy(
|
||||
backupMediaSize = getBackupMediaSize(),
|
||||
backupMediaSize = mediaSize,
|
||||
freeTierMediaRetentionDays = mediaRetentionDays,
|
||||
backupMediaDetails = if (RemoteConfig.internalUser || Environment.IS_STAGING) {
|
||||
RemoteBackupsSettingsState.BackupMediaDetails(
|
||||
awaitingRestore = SignalDatabase.attachments.getRemainingRestorableAttachmentSize().bytes,
|
||||
@@ -287,7 +295,7 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
|
||||
if (paidType is NetworkResult.Success) {
|
||||
val remoteStorageAllowance = paidType.result.storageAllowanceBytes.bytes
|
||||
val estimatedSize = SignalDatabase.attachments.getEstimatedArchiveMediaSize().bytes
|
||||
val estimatedSize = getBackupMediaSize(paidType.result.tier, paidType.result).first.bytes
|
||||
|
||||
if (estimatedSize + 300.mebiBytes <= remoteStorageAllowance) {
|
||||
BackupRepository.clearOutOfRemoteStorageSpaceError()
|
||||
@@ -303,13 +311,16 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
val (mediaSize, mediaRetentionDays) = getBackupMediaSize(_state.value.tier, (_state.value.backupState as? BackupState.WithTypeAndRenewalTime)?.messageBackupsType)
|
||||
|
||||
_state.update {
|
||||
it.copy(
|
||||
tier = SignalStore.backup.backupTier,
|
||||
backupsEnabled = SignalStore.backup.areBackupsEnabled,
|
||||
lastBackupTimestamp = SignalStore.backup.lastBackupTime,
|
||||
canBackupMessagesJobRun = BackupMessagesConstraint.isMet(AppDependencies.application),
|
||||
backupMediaSize = getBackupMediaSize(),
|
||||
backupMediaSize = mediaSize,
|
||||
freeTierMediaRetentionDays = mediaRetentionDays,
|
||||
canBackUpUsingCellular = SignalStore.backup.backupWithCellular,
|
||||
canRestoreUsingCellular = SignalStore.backup.restoreWithCellular,
|
||||
isOutOfStorageSpace = BackupRepository.shouldDisplayOutOfRemoteStorageSpaceUx(),
|
||||
@@ -320,11 +331,39 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBackupMediaSize(): Long {
|
||||
return if (SignalStore.backup.hasBackupBeenUploaded || SignalStore.backup.lastBackupTime > 0L) {
|
||||
SignalDatabase.attachments.getEstimatedArchiveMediaSize()
|
||||
private fun getBackupMediaSize(tier: MessageBackupTier?, messageBackupsType: MessageBackupsType?): Pair<Long, Int> {
|
||||
if (tier == null) {
|
||||
return -1L to 0
|
||||
}
|
||||
|
||||
val mediaRetentionDays = if (messageBackupsType is MessageBackupsType.Free) {
|
||||
messageBackupsType.mediaRetentionDays
|
||||
} else {
|
||||
0L
|
||||
when (tier) {
|
||||
MessageBackupTier.FREE -> {
|
||||
when (val result = BackupRepository.getFreeType()) {
|
||||
is NetworkResult.Success -> result.result.mediaRetentionDays
|
||||
else -> RemoteConfig.messageQueueTime.milliseconds.inWholeDays.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
MessageBackupTier.PAID -> 0
|
||||
}
|
||||
}
|
||||
|
||||
return if (SignalStore.backup.hasBackupBeenUploaded || SignalStore.backup.lastBackupTime > 0L) {
|
||||
when (tier) {
|
||||
MessageBackupTier.PAID -> SignalDatabase.attachments.getPaidEstimatedArchiveMediaSize() to -1
|
||||
MessageBackupTier.FREE -> {
|
||||
if (mediaRetentionDays > 0) {
|
||||
SignalDatabase.attachments.getFreeEstimatedArchiveMediaSize(System.currentTimeMillis() - mediaRetentionDays.days.inWholeMilliseconds) to mediaRetentionDays
|
||||
} else {
|
||||
-1L to -1
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
0L to mediaRetentionDays
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3009,8 +3009,17 @@ class AttachmentTable(
|
||||
.readToList { AttachmentId(it.requireLong(ID)) }
|
||||
}
|
||||
|
||||
fun getEstimatedArchiveMediaSize(): Long {
|
||||
val estimatedThumbnailCount = readableDatabase
|
||||
fun getPaidEstimatedArchiveMediaSize(): Long {
|
||||
return getEstimatedArchiveMediaSize()
|
||||
}
|
||||
|
||||
fun getFreeEstimatedArchiveMediaSize(afterTimestamp: Long): Long {
|
||||
return getEstimatedArchiveMediaSize(afterTimestamp)
|
||||
}
|
||||
|
||||
private fun getEstimatedArchiveMediaSize(afterTimestamp: Long = 0L): Long {
|
||||
val estimatedThumbnailCount = if (afterTimestamp == 0L) {
|
||||
readableDatabase
|
||||
.select("COUNT(*)")
|
||||
.from(
|
||||
"""
|
||||
@@ -3031,6 +3040,9 @@ class AttachmentTable(
|
||||
)
|
||||
.run()
|
||||
.readToSingleLong(0L)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
val uploadedAttachmentBytes = readableDatabase
|
||||
.rawQuery(
|
||||
@@ -3045,6 +3057,7 @@ class AttachmentTable(
|
||||
$REMOTE_KEY NOT NULL AND
|
||||
$TRANSFER_STATE = $TRANSFER_PROGRESS_DONE AND
|
||||
$ARCHIVE_TRANSFER_STATE != ${ArchiveTransferState.PERMANENT_FAILURE.value} AND
|
||||
${if (afterTimestamp > 0) "m.${MessageTable.DATE_RECEIVED} >= $afterTimestamp AND" else ""}
|
||||
${getMessageDoesNotExpireWithinTimeoutClause(tablePrefix = "m")}
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -166,7 +166,7 @@ class CopyAttachmentToArchiveJob private constructor(private val attachmentId: A
|
||||
Log.w(TAG, "[$attachmentId] Insufficient storage space! Can't upload!")
|
||||
val remoteStorageQuota = getServerQuota() ?: return Result.retry(defaultBackoff()).logW(TAG, "[$attachmentId] Failed to fetch server quota! Retrying.")
|
||||
|
||||
if (SignalDatabase.attachments.getEstimatedArchiveMediaSize() > remoteStorageQuota.inWholeBytes) {
|
||||
if (SignalDatabase.attachments.getPaidEstimatedArchiveMediaSize() > remoteStorageQuota.inWholeBytes) {
|
||||
BackupRepository.markOutOfRemoteStorageSpaceError()
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ object QuickRegistrationRepository {
|
||||
MessageBackupTier.FREE -> RegistrationProvisionMessage.Tier.FREE
|
||||
null -> null
|
||||
},
|
||||
backupSizeBytes = SignalDatabase.attachments.getEstimatedArchiveMediaSize().takeIf { it > 0 },
|
||||
backupSizeBytes = if (SignalStore.backup.backupTier == MessageBackupTier.PAID) SignalDatabase.attachments.getPaidEstimatedArchiveMediaSize().takeIf { it > 0 } else null,
|
||||
restoreMethodToken = restoreMethodToken,
|
||||
aciIdentityKeyPublic = SignalStore.account.aciIdentityKey.publicKey.serialize().toByteString(),
|
||||
aciIdentityKeyPrivate = SignalStore.account.aciIdentityKey.privateKey.serialize().toByteString(),
|
||||
|
||||
@@ -862,8 +862,10 @@
|
||||
<!-- CreateBackupBottomSheet -->
|
||||
<!-- Bottom sheet title -->
|
||||
<string name="CreateBackupBottomSheet__you_are_all_set">You\'re all set. Start your backup now.</string>
|
||||
<!-- Bottom sheet message -->
|
||||
<!-- Bottom sheet paid message -->
|
||||
<string name="CreateBackupBottomSheet__depending_on_the_size">Depending on the size of your backup, this could take a long time. You can use your phone as you normally do while the backup takes place.</string>
|
||||
<!-- Bottom sheet free message -->
|
||||
<string name="CreateBackupBottomSheet__free_tier">Media is added to your backup as you send and receive messages.</string>
|
||||
|
||||
<!-- Headline text for a bottom sheet dialog shown when the restoration of the media backup fails. -->
|
||||
<string name="RestoreMediaFailedBottomSheet__Cant_restore_media">Can\'t restore media</string>
|
||||
@@ -8188,6 +8190,13 @@
|
||||
<string name="RemoteBackupsSettingsFragment__to_view_your_key">To view your key, confirm it\'s you</string>
|
||||
<!-- Row label for cancelling and deleting backup -->
|
||||
<string name="RemoteBackupsSettingsFragment__turn_off_and_delete_backup">Turn off and delete backup</string>
|
||||
<!-- Dialog title for explainer text to on how backup size works for free tier -->
|
||||
<string name="RemoteBackupsSettingsFragment__free_tier_storage_title">Backup size</string>
|
||||
<!-- Dialog message for explainer text to on how backup size works for free tier -->
|
||||
<plurals name="RemoteBackupsSettingsFragment__backup_frequency_dialog_body">
|
||||
<item quantity="one">Your backup includes all of your text messages and your last %1$d day of media. The size will change as new media is received and old media expires.</item>
|
||||
<item quantity="other">Your backup includes all of your text messages and your last %1$d days of media. The size will change as new media is received and old media expires.</item>
|
||||
</plurals>
|
||||
<!-- Snackbar text displayed when backup has been deleted and turned off -->
|
||||
<string name="RemoteBackupsSettingsFragment__backup_deleted_and_turned_off">Backup deleted and turned off.</string>
|
||||
<!-- Snackbar text displayed when backup type is downgraded -->
|
||||
@@ -8331,6 +8340,8 @@
|
||||
<string name="RemoteBackupsSettingsFragment__a_network_error_occurred">A network error occurred. Please check your internet connection and try again.</string>
|
||||
<!-- Progress message when backup file is being uploaded. First placeholder and second placeholder are formatted byte sizes (2 MB) and third is percent completion. -->
|
||||
<string name="RemoteBackupsSettingsFragment__uploading_s_of_s_d">Uploading: %1$s of %2$s (%3$d%%)</string>
|
||||
<!-- Progress message when backup file is being uploaded. Placeholder is percent completion. -->
|
||||
<string name="RemoteBackupsSettingsFragment__uploading_d">Uploading: %1$d%%</string>
|
||||
<!-- Button label to see more details about redemption error -->
|
||||
<string name="RemoteBackupsSettingsFragment__details">Details</string>
|
||||
<!-- Text displayed when there was an error deleting backup -->
|
||||
|
||||
Reference in New Issue
Block a user