Integrate message backup frequency.

This commit is contained in:
Clark 2024-05-06 13:14:04 -04:00 committed by Alex Hart
parent de3b0d4ca2
commit 49ba83dda8
7 changed files with 70 additions and 37 deletions

View file

@ -0,0 +1,34 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2
import org.signal.core.util.LongSerializer
/**
* Describes how often a users messages are backed up.
*/
enum class BackupFrequency(val id: Int) {
DAILY(0),
WEEKLY(1),
MONTHLY(2),
MANUAL(-1);
companion object Serializer : LongSerializer<BackupFrequency> {
override fun serialize(data: BackupFrequency): Long {
return data.id.toLong()
}
override fun deserialize(data: Long): BackupFrequency {
return when (data.toInt()) {
MANUAL.id -> MANUAL
DAILY.id -> DAILY
WEEKLY.id -> WEEKLY
MONTHLY.id -> MONTHLY
else -> MANUAL
}
}
}
}

View file

@ -1,16 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2.ui.subscription
/**
* Describes how often a users messages are backed up.
*/
enum class MessageBackupsFrequency {
DAILY,
WEEKLY,
MONTHLY,
NEVER
}

View file

@ -51,10 +51,10 @@ import org.signal.core.ui.SignalPreview
import org.signal.core.ui.Snackbars import org.signal.core.ui.Snackbars
import org.signal.core.ui.Texts import org.signal.core.ui.Texts
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
import org.thoughtcrime.securesms.backup.v2.BackupV2Event import org.thoughtcrime.securesms.backup.v2.BackupV2Event
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowActivity import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowActivity
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency
import org.thoughtcrime.securesms.backup.v2.ui.subscription.getTierDetails import org.thoughtcrime.securesms.backup.v2.ui.subscription.getTierDetails
import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.compose.ComposeFragment
import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle
@ -132,7 +132,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.NONE) viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.NONE)
} }
override fun onSelectBackupsFrequencyChange(newFrequency: MessageBackupsFrequency) { override fun onSelectBackupsFrequencyChange(newFrequency: BackupFrequency) {
viewModel.setBackupsFrequency(newFrequency) viewModel.setBackupsFrequency(newFrequency)
} }
@ -170,7 +170,7 @@ private interface ContentCallbacks {
fun onChangeBackupFrequencyClick() = Unit fun onChangeBackupFrequencyClick() = Unit
fun onDialogDismissed() = Unit fun onDialogDismissed() = Unit
fun onSnackbarDismissed() = Unit fun onSnackbarDismissed() = Unit
fun onSelectBackupsFrequencyChange(newFrequency: MessageBackupsFrequency) = Unit fun onSelectBackupsFrequencyChange(newFrequency: BackupFrequency) = Unit
fun onTurnOffAndDeleteBackupsConfirm() = Unit fun onTurnOffAndDeleteBackupsConfirm() = Unit
} }
@ -179,7 +179,7 @@ private fun RemoteBackupsSettingsContent(
messageBackupTier: MessageBackupTier?, messageBackupTier: MessageBackupTier?,
lastBackupTimestamp: Long, lastBackupTimestamp: Long,
canBackUpUsingCellular: Boolean, canBackUpUsingCellular: Boolean,
backupsFrequency: MessageBackupsFrequency, backupsFrequency: BackupFrequency,
requestedDialog: RemoteBackupsSettingsState.Dialog, requestedDialog: RemoteBackupsSettingsState.Dialog,
requestedSnackbar: RemoteBackupsSettingsState.Snackbar, requestedSnackbar: RemoteBackupsSettingsState.Snackbar,
contentCallbacks: ContentCallbacks, contentCallbacks: ContentCallbacks,
@ -498,8 +498,8 @@ private fun TurnOffAndDeleteBackupsDialog(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun BackupFrequencyDialog( private fun BackupFrequencyDialog(
selected: MessageBackupsFrequency, selected: BackupFrequency,
onSelected: (MessageBackupsFrequency) -> Unit, onSelected: (BackupFrequency) -> Unit,
onDismiss: () -> Unit onDismiss: () -> Unit
) { ) {
AlertDialog( AlertDialog(
@ -520,12 +520,12 @@ private fun BackupFrequencyDialog(
modifier = Modifier.padding(24.dp) modifier = Modifier.padding(24.dp)
) )
MessageBackupsFrequency.values().forEach { BackupFrequency.values().forEach {
Rows.RadioRow( Rows.RadioRow(
selected = selected == it, selected = selected == it,
text = getTextForFrequency(backupsFrequency = it), text = getTextForFrequency(backupsFrequency = it),
label = when (it) { label = when (it) {
MessageBackupsFrequency.NEVER -> "By tapping \"Back up now\"" BackupFrequency.MANUAL -> "By tapping \"Back up now\""
else -> null else -> null
}, },
modifier = Modifier modifier = Modifier
@ -554,12 +554,12 @@ private fun BackupFrequencyDialog(
} }
@Composable @Composable
private fun getTextForFrequency(backupsFrequency: MessageBackupsFrequency): String { private fun getTextForFrequency(backupsFrequency: BackupFrequency): String {
return when (backupsFrequency) { return when (backupsFrequency) {
MessageBackupsFrequency.DAILY -> "Daily" BackupFrequency.DAILY -> "Daily"
MessageBackupsFrequency.WEEKLY -> "Weekly" BackupFrequency.WEEKLY -> "Weekly"
MessageBackupsFrequency.MONTHLY -> "Monthly" BackupFrequency.MONTHLY -> "Monthly"
MessageBackupsFrequency.NEVER -> "Manually back up" BackupFrequency.MANUAL -> "Manually back up"
} }
} }
@ -571,7 +571,7 @@ private fun RemoteBackupsSettingsContentPreview() {
messageBackupTier = null, messageBackupTier = null,
lastBackupTimestamp = -1, lastBackupTimestamp = -1,
canBackUpUsingCellular = false, canBackUpUsingCellular = false,
backupsFrequency = MessageBackupsFrequency.NEVER, backupsFrequency = BackupFrequency.MANUAL,
requestedDialog = RemoteBackupsSettingsState.Dialog.NONE, requestedDialog = RemoteBackupsSettingsState.Dialog.NONE,
requestedSnackbar = RemoteBackupsSettingsState.Snackbar.NONE, requestedSnackbar = RemoteBackupsSettingsState.Snackbar.NONE,
contentCallbacks = object : ContentCallbacks {}, contentCallbacks = object : ContentCallbacks {},
@ -628,7 +628,7 @@ private fun TurnOffAndDeleteBackupsDialogPreview() {
private fun BackupFrequencyDialogPreview() { private fun BackupFrequencyDialogPreview() {
Previews.Preview { Previews.Preview {
BackupFrequencyDialog( BackupFrequencyDialog(
selected = MessageBackupsFrequency.DAILY, selected = BackupFrequency.DAILY,
onSelected = {}, onSelected = {},
onDismiss = {} onDismiss = {}
) )

View file

@ -5,15 +5,15 @@
package org.thoughtcrime.securesms.components.settings.app.chats.backups package org.thoughtcrime.securesms.components.settings.app.chats.backups
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
import org.thoughtcrime.securesms.backup.v2.BackupV2Event import org.thoughtcrime.securesms.backup.v2.BackupV2Event
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency
data class RemoteBackupsSettingsState( data class RemoteBackupsSettingsState(
val messageBackupsTier: MessageBackupTier? = null, val messageBackupsTier: MessageBackupTier? = null,
val canBackUpUsingCellular: Boolean = false, val canBackUpUsingCellular: Boolean = false,
val backupSize: Long = 0, val backupSize: Long = 0,
val backupsFrequency: MessageBackupsFrequency = MessageBackupsFrequency.DAILY, val backupsFrequency: BackupFrequency = BackupFrequency.DAILY,
val lastBackupTimestamp: Long = 0, val lastBackupTimestamp: Long = 0,
val dialog: Dialog = Dialog.NONE, val dialog: Dialog = Dialog.NONE,
val snackbar: Snackbar = Snackbar.NONE, val snackbar: Snackbar = Snackbar.NONE,

View file

@ -8,11 +8,13 @@ package org.thoughtcrime.securesms.components.settings.app.chats.backups
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
import org.thoughtcrime.securesms.backup.v2.BackupV2Event import org.thoughtcrime.securesms.backup.v2.BackupV2Event
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupMessagesJob
import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.service.MessageBackupListener
/** /**
* ViewModel for state management of RemoteBackupsSettingsFragment * ViewModel for state management of RemoteBackupsSettingsFragment
@ -30,7 +32,8 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
null null
}, },
lastBackupTimestamp = SignalStore.backup().lastBackupTime, lastBackupTimestamp = SignalStore.backup().lastBackupTime,
backupSize = SignalStore.backup().totalBackupSize backupSize = SignalStore.backup().totalBackupSize,
backupsFrequency = SignalStore.backup().backupFrequency
) )
) )
@ -41,9 +44,11 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
internalState.value = state.value.copy(canBackUpUsingCellular = canBackUpUsingCellular) internalState.value = state.value.copy(canBackUpUsingCellular = canBackUpUsingCellular)
} }
fun setBackupsFrequency(backupsFrequency: MessageBackupsFrequency) { fun setBackupsFrequency(backupsFrequency: BackupFrequency) {
// TODO [message-backups] -- Update via repository? SignalStore.backup().backupFrequency = backupsFrequency
internalState.value = state.value.copy(backupsFrequency = backupsFrequency) internalState.value = state.value.copy(backupsFrequency = backupsFrequency)
MessageBackupListener.setNextBackupTimeToIntervalFromNow()
MessageBackupListener.schedule(ApplicationDependencies.getApplication())
} }
fun requestDialog(dialog: RemoteBackupsSettingsState.Dialog) { fun requestDialog(dialog: RemoteBackupsSettingsState.Dialog) {

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.keyvalue
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.backup.RestoreState import org.thoughtcrime.securesms.backup.RestoreState
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential
import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse import org.whispersystems.signalservice.api.archive.GetArchiveCdnCredentialsResponse
import org.whispersystems.signalservice.internal.util.JsonUtil import org.whispersystems.signalservice.internal.util.JsonUtil
@ -24,6 +25,7 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
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"
private const val KEY_BACKUP_FREQUENCY = "backup.backupFrequency"
private const val KEY_CDN_BACKUP_DIRECTORY = "backup.cdn.directory" private const val KEY_CDN_BACKUP_DIRECTORY = "backup.cdn.directory"
private const val KEY_CDN_BACKUP_MEDIA_DIRECTORY = "backup.cdn.mediaDirectory" private const val KEY_CDN_BACKUP_MEDIA_DIRECTORY = "backup.cdn.mediaDirectory"
@ -55,6 +57,7 @@ internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
var nextBackupTime: Long by longValue(KEY_NEXT_BACKUP_TIME, -1) var nextBackupTime: Long by longValue(KEY_NEXT_BACKUP_TIME, -1)
var lastBackupTime: Long by longValue(KEY_LAST_BACKUP_TIME, -1) var lastBackupTime: Long by longValue(KEY_LAST_BACKUP_TIME, -1)
var backupFrequency: BackupFrequency by enumValue(KEY_BACKUP_FREQUENCY, BackupFrequency.MANUAL, BackupFrequency.Serializer)
val totalBackupSize: Long get() = lastBackupProtoSize + usedBackupMediaSpace val totalBackupSize: Long get() = lastBackupProtoSize + usedBackupMediaSpace

View file

@ -6,6 +6,7 @@
package org.thoughtcrime.securesms.service package org.thoughtcrime.securesms.service
import android.content.Context import android.content.Context
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
import org.thoughtcrime.securesms.jobs.BackupMessagesJob import org.thoughtcrime.securesms.jobs.BackupMessagesJob
import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.FeatureFlags
@ -47,6 +48,12 @@ class MessageBackupListener : PersistentAlarmManagerListener() {
var next = now.withHour(hour).withMinute(minute).withSecond(0) var next = now.withHour(hour).withMinute(minute).withSecond(0)
val jitter = Random().nextInt(BACKUP_JITTER_WINDOW_SECONDS) - BACKUP_JITTER_WINDOW_SECONDS / 2 val jitter = Random().nextInt(BACKUP_JITTER_WINDOW_SECONDS) - BACKUP_JITTER_WINDOW_SECONDS / 2
next.plusSeconds(jitter.toLong()) next.plusSeconds(jitter.toLong())
next = when (SignalStore.backup().backupFrequency) {
BackupFrequency.DAILY -> next.plusDays(1)
BackupFrequency.MANUAL -> next.plusDays(365)
BackupFrequency.MONTHLY -> next.plusDays(30)
BackupFrequency.WEEKLY -> next.plusDays(7)
}
if (now.isAfter(next)) { if (now.isAfter(next)) {
next = next.plusDays(1) next = next.plusDays(1)
} }