mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Allow restoration over cellular
This commit is contained in:
committed by
Greyson Parrelli
parent
0c86ff1f84
commit
a7d7c1da8d
@@ -82,6 +82,7 @@ fun BackupStatusRow(
|
||||
is BackupStatusData.RestoringMedia -> {
|
||||
Text(
|
||||
text = getRestoringMediaString(backupStatusData),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(CoreUiR.dimen.gutter))
|
||||
)
|
||||
}
|
||||
@@ -168,15 +169,15 @@ private fun getRestoringMediaString(backupStatusData: BackupStatusData.Restoring
|
||||
return when (backupStatusData.restoreStatus) {
|
||||
BackupStatusData.RestoreStatus.NORMAL -> {
|
||||
stringResource(
|
||||
R.string.BackupStatusRow__downloading_s_of_s_s,
|
||||
R.string.BackupStatusRow__restoring_s_of_s_s,
|
||||
backupStatusData.bytesDownloaded.toUnitString(2),
|
||||
backupStatusData.bytesTotal.toUnitString(2),
|
||||
"%d".format((backupStatusData.progress * 100).roundToInt())
|
||||
)
|
||||
}
|
||||
BackupStatusData.RestoreStatus.LOW_BATTERY -> stringResource(R.string.BackupStatus__status_device_has_low_battery)
|
||||
BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET -> stringResource(R.string.BackupStatus__status_no_internet)
|
||||
BackupStatusData.RestoreStatus.WAITING_FOR_WIFI -> stringResource(R.string.BackupStatus__status_waiting_for_wifi)
|
||||
BackupStatusData.RestoreStatus.LOW_BATTERY -> stringResource(R.string.BackupStatusRow__restore_device_has_low_battery)
|
||||
BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET -> stringResource(R.string.BackupStatusRow__restore_no_internet)
|
||||
BackupStatusData.RestoreStatus.WAITING_FOR_WIFI -> stringResource(R.string.BackupStatusRow__restore_waiting_for_wifi)
|
||||
BackupStatusData.RestoreStatus.FINISHED -> stringResource(R.string.BackupStatus__restore_complete)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList
|
||||
.throttleLatest(1.seconds)
|
||||
.map {
|
||||
when {
|
||||
!WifiConstraint.isMet(AppDependencies.application) -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_WIFI)
|
||||
!WifiConstraint.isMet(AppDependencies.application) && !SignalStore.backup.restoreWithCellular -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_WIFI)
|
||||
!NetworkConstraint.isMet(AppDependencies.application) -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.WAITING_FOR_INTERNET)
|
||||
!BatteryNotLowConstraint.isMet() -> BackupStatusData.RestoringMedia(restoreStatus = BackupStatusData.RestoreStatus.LOW_BATTERY)
|
||||
else -> {
|
||||
|
||||
@@ -141,6 +141,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
backupsEnabled = state.backupsEnabled,
|
||||
lastBackupTimestamp = state.lastBackupTimestamp,
|
||||
canBackUpUsingCellular = state.canBackUpUsingCellular,
|
||||
canRestoreUsingCellular = state.canRestoreUsingCellular,
|
||||
backupsFrequency = state.backupsFrequency,
|
||||
requestedDialog = state.dialog,
|
||||
requestedSnackbar = state.snackbar,
|
||||
@@ -239,6 +240,10 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
override fun onLearnMoreAboutBackupFailure() {
|
||||
BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(parentFragmentManager, null)
|
||||
}
|
||||
|
||||
override fun onRestoreUsingCellularClick(canUseCellular: Boolean) {
|
||||
viewModel.setCanRestoreUsingCellular(canUseCellular)
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayBackupKey() {
|
||||
@@ -321,6 +326,7 @@ private interface ContentCallbacks {
|
||||
fun onLearnMoreAboutLostSubscription() = Unit
|
||||
fun onContactSupport() = Unit
|
||||
fun onLearnMoreAboutBackupFailure() = Unit
|
||||
fun onRestoreUsingCellularClick(canUseCellular: Boolean) = Unit
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -330,6 +336,7 @@ private fun RemoteBackupsSettingsContent(
|
||||
backupRestoreState: BackupRestoreState,
|
||||
lastBackupTimestamp: Long,
|
||||
canBackUpUsingCellular: Boolean,
|
||||
canRestoreUsingCellular: Boolean,
|
||||
backupsFrequency: BackupFrequency,
|
||||
requestedDialog: RemoteBackupsSettingsState.Dialog,
|
||||
requestedSnackbar: RemoteBackupsSettingsState.Snackbar,
|
||||
@@ -403,6 +410,14 @@ private fun RemoteBackupsSettingsContent(
|
||||
onLearnMoreClick = contentCallbacks::onLearnMoreAboutBackupFailure
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Rows.ToggleRow(
|
||||
checked = canRestoreUsingCellular,
|
||||
text = stringResource(id = R.string.RemoteBackupsSettingsFragment__restore_using_cellular),
|
||||
onCheckChanged = contentCallbacks::onRestoreUsingCellularClick
|
||||
)
|
||||
}
|
||||
} else if (backupRestoreState is BackupRestoreState.Ready && backupState is RemoteBackupsSettingsState.BackupState.Canceled) {
|
||||
item {
|
||||
BackupReadyToDownloadRow(
|
||||
@@ -420,6 +435,7 @@ private fun RemoteBackupsSettingsContent(
|
||||
backupSize = backupSize,
|
||||
backupsFrequency = backupsFrequency,
|
||||
canBackUpUsingCellular = canBackUpUsingCellular,
|
||||
canRestoreUsingCellular = canRestoreUsingCellular,
|
||||
contentCallbacks = contentCallbacks
|
||||
)
|
||||
} else {
|
||||
@@ -540,6 +556,7 @@ private fun LazyListScope.appendBackupDetailsItems(
|
||||
backupSize: Long,
|
||||
backupsFrequency: BackupFrequency,
|
||||
canBackUpUsingCellular: Boolean,
|
||||
canRestoreUsingCellular: Boolean,
|
||||
contentCallbacks: ContentCallbacks
|
||||
) {
|
||||
item {
|
||||
@@ -1205,6 +1222,7 @@ private fun RemoteBackupsSettingsContentPreview() {
|
||||
backupsEnabled = true,
|
||||
lastBackupTimestamp = -1,
|
||||
canBackUpUsingCellular = false,
|
||||
canRestoreUsingCellular = false,
|
||||
backupsFrequency = BackupFrequency.MANUAL,
|
||||
requestedDialog = RemoteBackupsSettingsState.Dialog.NONE,
|
||||
requestedSnackbar = RemoteBackupsSettingsState.Snackbar.NONE,
|
||||
|
||||
@@ -14,6 +14,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
data class RemoteBackupsSettingsState(
|
||||
val backupsEnabled: Boolean,
|
||||
val canBackUpUsingCellular: Boolean = false,
|
||||
val canRestoreUsingCellular: Boolean = false,
|
||||
val backupState: BackupState = BackupState.Loading,
|
||||
val backupSize: Long = 0,
|
||||
val backupsFrequency: BackupFrequency = BackupFrequency.DAILY,
|
||||
|
||||
@@ -61,7 +61,8 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
lastBackupTimestamp = SignalStore.backup.lastBackupTime,
|
||||
backupSize = SignalStore.backup.totalBackupSize,
|
||||
backupsFrequency = SignalStore.backup.backupFrequency,
|
||||
canBackUpUsingCellular = SignalStore.backup.backupWithCellular
|
||||
canBackUpUsingCellular = SignalStore.backup.backupWithCellular,
|
||||
canRestoreUsingCellular = SignalStore.backup.restoreWithCellular
|
||||
)
|
||||
)
|
||||
|
||||
@@ -109,6 +110,11 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
_state.update { it.copy(canBackUpUsingCellular = canBackUpUsingCellular) }
|
||||
}
|
||||
|
||||
fun setCanRestoreUsingCellular(canRestoreUsingCellular: Boolean) {
|
||||
SignalStore.backup.restoreWithCellular = canRestoreUsingCellular
|
||||
_state.update { it.copy(canRestoreUsingCellular = canRestoreUsingCellular) }
|
||||
}
|
||||
|
||||
fun setBackupsFrequency(backupsFrequency: BackupFrequency) {
|
||||
SignalStore.backup.backupFrequency = backupsFrequency
|
||||
_state.update { it.copy(backupsFrequency = backupsFrequency) }
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.jobmanager.impl
|
||||
|
||||
import android.app.Application
|
||||
import android.app.job.JobInfo
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.jobmanager.Constraint
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
/**
|
||||
* Constraint that, when added, means that a job cannot be performed unless the user either has Wifi or, if they enabled it, cellular
|
||||
*/
|
||||
class RestoreAttachmentConstraint(private val application: Application) : Constraint {
|
||||
|
||||
companion object {
|
||||
const val KEY = "RestoreAttachmentConstraint"
|
||||
|
||||
fun isMet(context: Context): Boolean {
|
||||
if (SignalStore.backup.restoreWithCellular) {
|
||||
return NetworkConstraint.isMet(context)
|
||||
}
|
||||
return WifiConstraint.isMet(context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isMet(): Boolean {
|
||||
return isMet(application)
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun applyToJobInfo(jobInfoBuilder: JobInfo.Builder) = Unit
|
||||
|
||||
class Factory(val application: Application) : Constraint.Factory<RestoreAttachmentConstraint> {
|
||||
override fun create(): RestoreAttachmentConstraint {
|
||||
return RestoreAttachmentConstraint(application)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.impl
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.ConstraintObserver
|
||||
|
||||
/**
|
||||
* An observer for the [RestoreAttachmentConstraint]. This is called
|
||||
* when users change whether or not restoring is allowed via cellular
|
||||
*/
|
||||
object RestoreAttachmentConstraintObserver : ConstraintObserver {
|
||||
|
||||
private const val REASON = "RestoreAttachmentConstraint"
|
||||
|
||||
private var notifier: ConstraintObserver.Notifier? = null
|
||||
|
||||
override fun register(notifier: ConstraintObserver.Notifier) {
|
||||
this.notifier = notifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Let the observer know that the restore using cellular flag has changed.
|
||||
*/
|
||||
fun onChange() {
|
||||
if (RestoreAttachmentConstraint.isMet(AppDependencies.application)) {
|
||||
notifier?.onConstraintMet(REASON)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NotInCallConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NotInCallConstraintObserver;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraintObserver;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.WifiConstraint;
|
||||
@@ -386,6 +388,7 @@ public final class JobManagerFactories {
|
||||
put(NotInCallConstraint.KEY, new NotInCallConstraint.Factory());
|
||||
put(SqlCipherMigrationConstraint.KEY, new SqlCipherMigrationConstraint.Factory(application));
|
||||
put(WifiConstraint.KEY, new WifiConstraint.Factory(application));
|
||||
put(RestoreAttachmentConstraint.KEY, new RestoreAttachmentConstraint.Factory(application));
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -397,7 +400,8 @@ public final class JobManagerFactories {
|
||||
new DecryptionsDrainedConstraintObserver(),
|
||||
new NotInCallConstraintObserver(),
|
||||
ChangeNumberConstraintObserver.INSTANCE,
|
||||
DataRestoreConstraintObserver.INSTANCE);
|
||||
DataRestoreConstraintObserver.INSTANCE,
|
||||
RestoreAttachmentConstraintObserver.INSTANCE);
|
||||
}
|
||||
|
||||
public static List<JobMigration> getJobMigrations(@NonNull Application application) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.events.PartProgressEvent
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.JobLogger.format
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.BatteryNotLowConstraint
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.WifiConstraint
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraint
|
||||
import org.thoughtcrime.securesms.jobs.protos.RestoreAttachmentJobData
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mms.MmsException
|
||||
@@ -108,7 +108,7 @@ class RestoreAttachmentJob private constructor(
|
||||
private constructor(messageId: Long, attachmentId: AttachmentId, manual: Boolean, queue: String) : this(
|
||||
Parameters.Builder()
|
||||
.setQueue(queue)
|
||||
.addConstraint(WifiConstraint.KEY)
|
||||
.addConstraint(RestoreAttachmentConstraint.KEY)
|
||||
.addConstraint(BatteryNotLowConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
||||
.build(),
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.backup.RestoreState
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraintObserver
|
||||
import org.thoughtcrime.securesms.keyvalue.protos.ArchiveUploadProgressState
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential
|
||||
@@ -48,6 +49,7 @@ class BackupValues(store: KeyValueStore) : SignalStoreValues(store) {
|
||||
private const val KEY_CDN_MEDIA_PATH = "backup.cdn.mediaPath"
|
||||
|
||||
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"
|
||||
private const val KEY_BACKUPS_INITIALIZED = "backup.initialized"
|
||||
|
||||
@@ -82,6 +84,13 @@ 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 restoreWithCellular: Boolean
|
||||
get() = getBoolean(KEY_RESTORE_OVER_CELLULAR, false)
|
||||
set(value) {
|
||||
putBoolean(KEY_RESTORE_OVER_CELLULAR, value)
|
||||
RestoreAttachmentConstraintObserver.onChange()
|
||||
}
|
||||
|
||||
var nextBackupTime: Long by longValue(KEY_NEXT_BACKUP_TIME, -1)
|
||||
var lastBackupTime: Long
|
||||
get() = getLong(KEY_LAST_BACKUP_TIME, -1)
|
||||
|
||||
@@ -7542,7 +7542,13 @@
|
||||
<!-- Content description for x icon at the end of the linear progress indicator -->
|
||||
<string name="BackupStatusRow__cancel_download">Cancel download</string>
|
||||
<!-- Backup progress. Placeholders are size restored, size to restore, and percent, i.e. 50MB of 100MB (50%) -->
|
||||
<string name="BackupStatusRow__downloading_s_of_s_s">Downloading: %1$s of %2$s (%3$s%%)</string>
|
||||
<string name="BackupStatusRow__restoring_s_of_s_s">Restoring: %1$s of %2$s (%3$s%%)</string>
|
||||
<!-- Text displayed under progress bar when restoring media pauses for Wi-Fi -->
|
||||
<string name="BackupStatusRow__restore_waiting_for_wifi">Restore paused: Waiting for Wi-Fi…</string>
|
||||
<!-- Text displayed under progress bar when restoring media pauses for internet -->
|
||||
<string name="BackupStatusRow__restore_no_internet">Restore paused: No internet…</string>
|
||||
<!-- Text displayed under progress bar when restoring media pauses for low battery -->
|
||||
<string name="BackupStatusRow__restore_device_has_low_battery">Restore paused: Device has low battery</string>
|
||||
<!-- Notice that there is not enough free space to continue restore. Placeholder is required space, for example 1.6GB -->
|
||||
<string name="BackupStatusRow__not_enough_space">Not enough space to download your Backup. To continue free up %1$s of space.</string>
|
||||
<!-- Text row label to skip download -->
|
||||
@@ -7651,6 +7657,8 @@
|
||||
<string name="RemoteBackupsSettingsFragment__payment_history">Payment history</string>
|
||||
<!-- Section header for backup information -->
|
||||
<string name="RemoteBackupsSettingsFragment__backup_details">Backup details</string>
|
||||
<!-- Toggle row label to allow the restoration of a backup to occur while on cellular connection -->
|
||||
<string name="RemoteBackupsSettingsFragment__restore_using_cellular">Restore using cellular</string>
|
||||
<!-- Row label for backup size -->
|
||||
<string name="RemoteBackupsSettingsFragment__backup_size">Backup size</string>
|
||||
<!-- Row label for backup frequency -->
|
||||
|
||||
Reference in New Issue
Block a user