mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Basic settings functionality for message backup.
This commit is contained in:
@@ -60,8 +60,12 @@ class ChatsSettingsViewModel @JvmOverloads constructor(
|
||||
|
||||
fun refresh() {
|
||||
val backupsEnabled = SignalStore.settings().isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(ApplicationDependencies.getApplication())
|
||||
if (store.state.localBackupsEnabled != backupsEnabled) {
|
||||
store.update { it.copy(localBackupsEnabled = backupsEnabled) }
|
||||
val remoteBackupsEnabled = SignalStore.backup().areBackupsEnabled
|
||||
|
||||
if (store.state.localBackupsEnabled != backupsEnabled ||
|
||||
store.state.remoteBackupsEnabled != remoteBackupsEnabled
|
||||
) {
|
||||
store.update { it.copy(localBackupsEnabled = backupsEnabled, remoteBackupsEnabled = remoteBackupsEnabled) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.backups
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -17,8 +19,10 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -34,7 +38,9 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.signal.core.ui.Buttons
|
||||
import org.signal.core.ui.Dialogs
|
||||
import org.signal.core.ui.Dividers
|
||||
@@ -44,18 +50,18 @@ import org.signal.core.ui.Scaffolds
|
||||
import org.signal.core.ui.SignalPreview
|
||||
import org.signal.core.ui.Snackbars
|
||||
import org.signal.core.ui.Texts
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupV2Event
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
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.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.getTierDetails
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
import org.thoughtcrime.securesms.util.DateUtils
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
@@ -75,13 +81,14 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
val callbacks = remember { Callbacks() }
|
||||
|
||||
RemoteBackupsSettingsContent(
|
||||
messageBackupsType = state.messageBackupsType,
|
||||
messageBackupTier = state.messageBackupsTier,
|
||||
lastBackupTimestamp = state.lastBackupTimestamp,
|
||||
canBackUpUsingCellular = state.canBackUpUsingCellular,
|
||||
backupsFrequency = state.backupsFrequency,
|
||||
requestedDialog = state.dialog,
|
||||
requestedSnackbar = state.snackbar,
|
||||
contentCallbacks = callbacks
|
||||
contentCallbacks = callbacks,
|
||||
backupProgress = state.backupProgress
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,7 +111,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
}
|
||||
|
||||
override fun onBackupNowClick() {
|
||||
// TODO [message-backups] Enqueue immediate backup
|
||||
viewModel.onBackupNowClick()
|
||||
}
|
||||
|
||||
override fun onTurnOffAndDeleteBackupsClick() {
|
||||
@@ -135,6 +142,16 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
findNavController().safeNavigate(R.id.action_remoteBackupsSettingsFragment_to_backupsTypeSettingsFragment)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onEvent(backupEvent: BackupV2Event) {
|
||||
viewModel.updateBackupProgress(backupEvent)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
EventBus.getDefault().registerForLifecycle(subscriber = this, lifecycleOwner = viewLifecycleOwner)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,13 +174,14 @@ private interface ContentCallbacks {
|
||||
|
||||
@Composable
|
||||
private fun RemoteBackupsSettingsContent(
|
||||
messageBackupsType: MessageBackupsType?,
|
||||
messageBackupTier: MessageBackupTier?,
|
||||
lastBackupTimestamp: Long,
|
||||
canBackUpUsingCellular: Boolean,
|
||||
backupsFrequency: MessageBackupsFrequency,
|
||||
requestedDialog: RemoteBackupsSettingsState.Dialog,
|
||||
requestedSnackbar: RemoteBackupsSettingsState.Snackbar,
|
||||
contentCallbacks: ContentCallbacks
|
||||
contentCallbacks: ContentCallbacks,
|
||||
backupProgress: BackupV2Event?
|
||||
) {
|
||||
val snackbarHostState = remember {
|
||||
SnackbarHostState()
|
||||
@@ -183,13 +201,13 @@ private fun RemoteBackupsSettingsContent(
|
||||
) {
|
||||
item {
|
||||
BackupTypeRow(
|
||||
messageBackupsType = messageBackupsType,
|
||||
messageBackupTier = messageBackupTier,
|
||||
onEnableBackupsClick = contentCallbacks::onEnableBackupsClick,
|
||||
onChangeBackupsTypeClick = contentCallbacks::onBackupsTypeClick
|
||||
)
|
||||
}
|
||||
|
||||
if (messageBackupsType == null) {
|
||||
if (messageBackupTier == null) {
|
||||
item {
|
||||
Rows.TextRow(
|
||||
text = "Payment history",
|
||||
@@ -205,11 +223,17 @@ private fun RemoteBackupsSettingsContent(
|
||||
Texts.SectionHeader(text = "Backup Details")
|
||||
}
|
||||
|
||||
item {
|
||||
LastBackupRow(
|
||||
lastBackupTimestamp = lastBackupTimestamp,
|
||||
onBackupNowClick = {}
|
||||
)
|
||||
if (backupProgress == null || backupProgress.type == BackupV2Event.Type.FINISHED) {
|
||||
item {
|
||||
LastBackupRow(
|
||||
lastBackupTimestamp = lastBackupTimestamp,
|
||||
onBackupNowClick = contentCallbacks::onBackupNowClick
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
InProgressBackupRow(progress = backupProgress.count.toInt(), totalProgress = backupProgress.estimatedTotalCount.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -326,14 +350,16 @@ private fun RemoteBackupsSettingsContent(
|
||||
|
||||
@Composable
|
||||
private fun BackupTypeRow(
|
||||
messageBackupsType: MessageBackupsType?,
|
||||
messageBackupTier: MessageBackupTier?,
|
||||
onEnableBackupsClick: () -> Unit,
|
||||
onChangeBackupsTypeClick: () -> Unit
|
||||
) {
|
||||
val messageBackupsType = if (messageBackupTier != null) getTierDetails(messageBackupTier) else null
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = messageBackupsType != null, onClick = onChangeBackupsTypeClick)
|
||||
.clickable(enabled = messageBackupTier != null, onClick = onChangeBackupsTypeClick)
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(top = 16.dp, bottom = 14.dp)
|
||||
) {
|
||||
@@ -372,6 +398,34 @@ private fun BackupTypeRow(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InProgressBackupRow(
|
||||
progress: Int?,
|
||||
totalProgress: Int?
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(top = 16.dp, bottom = 14.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
if (totalProgress == null || totalProgress == 0) {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
} else {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth(), progress = ((progress ?: 0) / totalProgress).toFloat())
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "$progress/$totalProgress",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LastBackupRow(
|
||||
lastBackupTimestamp: Long,
|
||||
@@ -448,46 +502,48 @@ private fun BackupFrequencyDialog(
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = AlertDialogDefaults.containerColor,
|
||||
shape = AlertDialogDefaults.shape
|
||||
)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = "Backup frequency",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.padding(24.dp)
|
||||
)
|
||||
|
||||
MessageBackupsFrequency.values().forEach {
|
||||
Rows.RadioRow(
|
||||
selected = selected == it,
|
||||
text = getTextForFrequency(backupsFrequency = it),
|
||||
label = when (it) {
|
||||
MessageBackupsFrequency.NEVER -> "By tapping \"Back up now\""
|
||||
else -> null
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(end = 24.dp)
|
||||
.clickable(onClick = {
|
||||
onSelected(it)
|
||||
onDismiss()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.CenterEnd,
|
||||
Surface {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = AlertDialogDefaults.containerColor,
|
||||
shape = AlertDialogDefaults.shape
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp)
|
||||
.padding(bottom = 24.dp)
|
||||
) {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(id = android.R.string.cancel))
|
||||
Text(
|
||||
text = "Backup frequency",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.padding(24.dp)
|
||||
)
|
||||
|
||||
MessageBackupsFrequency.values().forEach {
|
||||
Rows.RadioRow(
|
||||
selected = selected == it,
|
||||
text = getTextForFrequency(backupsFrequency = it),
|
||||
label = when (it) {
|
||||
MessageBackupsFrequency.NEVER -> "By tapping \"Back up now\""
|
||||
else -> null
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(end = 24.dp)
|
||||
.clickable(onClick = {
|
||||
onSelected(it)
|
||||
onDismiss()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.CenterEnd,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp)
|
||||
.padding(bottom = 24.dp)
|
||||
) {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(id = android.R.string.cancel))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,13 +565,14 @@ private fun getTextForFrequency(backupsFrequency: MessageBackupsFrequency): Stri
|
||||
private fun RemoteBackupsSettingsContentPreview() {
|
||||
Previews.Preview {
|
||||
RemoteBackupsSettingsContent(
|
||||
messageBackupsType = null,
|
||||
messageBackupTier = null,
|
||||
lastBackupTimestamp = -1,
|
||||
canBackUpUsingCellular = false,
|
||||
backupsFrequency = MessageBackupsFrequency.NEVER,
|
||||
requestedDialog = RemoteBackupsSettingsState.Dialog.NONE,
|
||||
requestedSnackbar = RemoteBackupsSettingsState.Snackbar.NONE,
|
||||
contentCallbacks = object : ContentCallbacks {}
|
||||
contentCallbacks = object : ContentCallbacks {},
|
||||
backupProgress = null
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -525,11 +582,7 @@ private fun RemoteBackupsSettingsContentPreview() {
|
||||
private fun BackupTypeRowPreview() {
|
||||
Previews.Preview {
|
||||
BackupTypeRow(
|
||||
messageBackupsType = MessageBackupsType(
|
||||
title = "Text + all media",
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3L), Currency.getInstance(Locale.US)),
|
||||
features = persistentListOf()
|
||||
),
|
||||
messageBackupTier = MessageBackupTier.PAID,
|
||||
onChangeBackupsTypeClick = {},
|
||||
onEnableBackupsClick = {}
|
||||
)
|
||||
@@ -547,6 +600,14 @@ private fun LastBackupRowPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun InProgressRowPreview() {
|
||||
Previews.Preview {
|
||||
InProgressBackupRow(50, 100)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun TurnOffAndDeleteBackupsDialogPreview() {
|
||||
|
||||
@@ -5,17 +5,19 @@
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.backups
|
||||
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupV2Event
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
|
||||
data class RemoteBackupsSettingsState(
|
||||
val messageBackupsType: MessageBackupsType? = null,
|
||||
val messageBackupsTier: MessageBackupTier? = null,
|
||||
val canBackUpUsingCellular: Boolean = false,
|
||||
val backupSize: Long = 0,
|
||||
val backupsFrequency: MessageBackupsFrequency = MessageBackupsFrequency.DAILY,
|
||||
val lastBackupTimestamp: Long = 0,
|
||||
val dialog: Dialog = Dialog.NONE,
|
||||
val snackbar: Snackbar = Snackbar.NONE
|
||||
val snackbar: Snackbar = Snackbar.NONE,
|
||||
val backupProgress: BackupV2Event? = null
|
||||
) {
|
||||
enum class Dialog {
|
||||
NONE,
|
||||
|
||||
@@ -8,13 +8,30 @@ package org.thoughtcrime.securesms.components.settings.app.chats.backups
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupV2Event
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFrequency
|
||||
import org.thoughtcrime.securesms.jobs.BackupMessagesJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
/**
|
||||
* ViewModel for state management of RemoteBackupsSettingsFragment
|
||||
*/
|
||||
class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
private val internalState = mutableStateOf(RemoteBackupsSettingsState())
|
||||
private val internalState = mutableStateOf(
|
||||
RemoteBackupsSettingsState(
|
||||
messageBackupsTier = if (SignalStore.backup().areBackupsEnabled) {
|
||||
if (SignalStore.backup().canReadWriteToArchiveCdn) {
|
||||
MessageBackupTier.PAID
|
||||
} else {
|
||||
MessageBackupTier.FREE
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
lastBackupTimestamp = SignalStore.backup().lastBackupTime
|
||||
)
|
||||
)
|
||||
|
||||
val state: State<RemoteBackupsSettingsState> = internalState
|
||||
|
||||
@@ -38,6 +55,17 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
|
||||
fun turnOffAndDeleteBackups() {
|
||||
// TODO [message-backups] -- Delete.
|
||||
SignalStore.backup().areBackupsEnabled = false
|
||||
internalState.value = state.value.copy(snackbar = RemoteBackupsSettingsState.Snackbar.BACKUP_DELETED_AND_TURNED_OFF)
|
||||
}
|
||||
|
||||
fun updateBackupProgress(backupEvent: BackupV2Event?) {
|
||||
internalState.value = state.value.copy(backupProgress = backupEvent, lastBackupTimestamp = SignalStore.backup().lastBackupTime)
|
||||
}
|
||||
|
||||
fun onBackupNowClick() {
|
||||
if (state.value.backupProgress == null || state.value.backupProgress?.type == BackupV2Event.Type.FINISHED) {
|
||||
BackupMessagesJob.enqueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.signal.core.ui.SignalPreview
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.donations.PaymentSourceType
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowActivity
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
@@ -186,6 +187,7 @@ private fun BackupsTypeSettingsContentPreview() {
|
||||
BackupsTypeSettingsContent(
|
||||
state = BackupsTypeSettingsState(
|
||||
backupsType = MessageBackupsType(
|
||||
tier = MessageBackupTier.PAID,
|
||||
pricePerMonth = FiatMoney(BigDecimal.valueOf(3), Currency.getInstance("USD")),
|
||||
title = "Text + all media",
|
||||
features = persistentListOf()
|
||||
|
||||
Reference in New Issue
Block a user