diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index a23b2e5e9b..bd77ff0ba1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -69,8 +69,10 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobs.AvatarGroupsV2DownloadJob +import org.thoughtcrime.securesms.jobs.CheckRestoreMediaLeftJob import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.jobs.RestoreAttachmentJob +import org.thoughtcrime.securesms.jobs.RestoreOptimizedMediaJob import org.thoughtcrime.securesms.keyvalue.BackupValues.ArchiveServiceCredentials import org.thoughtcrime.securesms.keyvalue.KeyValueStore import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -196,6 +198,12 @@ object BackupRepository { } } + @JvmStatic + fun resumeMediaRestore() { + SignalStore.backup.userManuallySkippedMediaRestore = false + RestoreOptimizedMediaJob.enqueue() + } + /** * Cancels any relevant jobs for media restore */ @@ -206,6 +214,10 @@ object BackupRepository { AppDependencies.jobManager.cancelAllInQueue(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.RESTORE_OFFLOADED)) AppDependencies.jobManager.cancelAllInQueue(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.INITIAL_RESTORE)) AppDependencies.jobManager.cancelAllInQueue(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.MANUAL)) + + AppDependencies.jobManager.add(CheckRestoreMediaLeftJob(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.RESTORE_OFFLOADED))) + AppDependencies.jobManager.add(CheckRestoreMediaLeftJob(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.INITIAL_RESTORE))) + AppDependencies.jobManager.add(CheckRestoreMediaLeftJob(RestoreAttachmentJob.constructQueueString(RestoreAttachmentJob.RestoreOperation.MANUAL))) } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt index 6a0d1f5d53..623979e654 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/BackupAlertBottomSheet.kt @@ -55,9 +55,7 @@ import org.thoughtcrime.securesms.backup.v2.BackupRepository import org.thoughtcrime.securesms.billing.launchManageBackupsSubscription import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment -import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobs.BackupMessagesJob -import org.thoughtcrime.securesms.jobs.BackupRestoreMediaJob import org.thoughtcrime.securesms.util.CommunicationActions import org.thoughtcrime.securesms.util.PlayStoreUtil import org.signal.core.ui.R as CoreUiR @@ -185,8 +183,7 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() { } private fun performFullMediaDownload() { - // TODO [backups] -- We need to force this to download everything - AppDependencies.jobManager.add(BackupRestoreMediaJob()) + BackupRepository.resumeMediaRestore() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt index 24963cfd38..dc757021ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusBanner.kt @@ -283,8 +283,18 @@ sealed interface BackupStatusData { val restoreStatus: RestoreStatus = RestoreStatus.NORMAL ) : BackupStatusData { override val iconRes: Int = if (restoreStatus == RestoreStatus.FINISHED) R.drawable.symbol_check_circle_24 else R.drawable.symbol_backup_light - override val iconColors: BackupsIconColors = if (restoreStatus == RestoreStatus.FINISHED) BackupsIconColors.Success else BackupsIconColors.Normal + override val iconColors: BackupsIconColors = when (restoreStatus) { + RestoreStatus.FINISHED -> BackupsIconColors.Success + RestoreStatus.NORMAL -> BackupsIconColors.Normal + RestoreStatus.LOW_BATTERY, + RestoreStatus.WAITING_FOR_INTERNET, + RestoreStatus.WAITING_FOR_WIFI -> BackupsIconColors.Warning + } override val showDismissAction: Boolean = restoreStatus == RestoreStatus.FINISHED + override val actionRes: Int = when (restoreStatus) { + RestoreStatus.WAITING_FOR_WIFI -> R.string.BackupStatus__resume + else -> NONE + } override val title: String @Composable get() = stringResource( @@ -311,7 +321,7 @@ sealed interface BackupStatusData { RestoreStatus.FINISHED -> bytesTotal.toUnitString() } - override val progress: Float = if (bytesTotal.bytes > 0 && restoreStatus != RestoreStatus.FINISHED) { + override val progress: Float = if (bytesTotal.bytes > 0 && restoreStatus == RestoreStatus.NORMAL) { min(1f, max(0f, bytesDownloaded.bytes.toFloat() / bytesTotal.bytes.toFloat())) } else { NONE.toFloat() diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt index 4ea6661113..042fc654dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/status/BackupStatusRow.kt @@ -184,9 +184,10 @@ private fun getRestoringMediaString(backupStatusData: BackupStatusData.Restoring @Composable private fun progressColor(backupStatusData: BackupStatusData): Color { - return when (backupStatusData) { - is BackupStatusData.RestoringMedia -> MaterialTheme.colorScheme.primary - else -> backupStatusData.iconColors.foreground + return if (backupStatusData is BackupStatusData.RestoringMedia && backupStatusData.restoreStatus == BackupStatusData.RestoreStatus.NORMAL) { + MaterialTheme.colorScheme.primary + } else { + backupStatusData.iconColors.foreground } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/banner/banners/MediaRestoreProgressBanner.kt b/app/src/main/java/org/thoughtcrime/securesms/banner/banners/MediaRestoreProgressBanner.kt index 09a934da1d..2362ab0125 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/banner/banners/MediaRestoreProgressBanner.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/banner/banners/MediaRestoreProgressBanner.kt @@ -58,7 +58,7 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList totalRestoredSize > 0 -> { flowOf( BackupStatusData.RestoringMedia( - bytesTotal = totalRestoredSize.bytes.also { totalRestoredSize = 0 }, + bytesTotal = totalRestoredSize.bytes, restoreStatus = BackupStatusData.RestoreStatus.FINISHED ) ) @@ -75,7 +75,10 @@ class MediaRestoreProgressBanner(private val listener: RestoreProgressBannerList data = model, onBannerClick = listener::onBannerClick, onActionClick = listener::onActionClick, - onDismissClick = listener::onDismissComplete + onDismissClick = { + totalRestoredSize = 0 + listener.onDismissComplete() + } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt index 4da050972b..4fbc63c1fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsFragment.kt @@ -217,7 +217,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { } override fun onCancelMediaRestore() { - viewModel.cancelMediaRestore() + viewModel.requestDialog(RemoteBackupsSettingsState.Dialog.CANCEL_MEDIA_RESTORE_PROTECTION) } override fun onDisplaySkipMediaRestoreProtectionDialog() { @@ -245,8 +245,12 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { BackupAlertBottomSheet.create(BackupAlert.BackupFailed).show(parentFragmentManager, null) } - override fun onRestoreUsingCellularClick(canUseCellular: Boolean) { - viewModel.setCanRestoreUsingCellular(canUseCellular) + override fun onRestoreUsingCellularConfirm() { + viewModel.requestDialog(RemoteBackupsSettingsState.Dialog.RESTORE_OVER_CELLULAR_PROTECTION) + } + + override fun onRestoreUsingCellularClick() { + viewModel.setCanRestoreUsingCellular() } override fun onRedemptionErrorDetailsClick() { @@ -334,7 +338,8 @@ private interface ContentCallbacks { fun onLearnMoreAboutLostSubscription() = Unit fun onContactSupport() = Unit fun onLearnMoreAboutBackupFailure() = Unit - fun onRestoreUsingCellularClick(canUseCellular: Boolean) = Unit + fun onRestoreUsingCellularConfirm() = Unit + fun onRestoreUsingCellularClick() = Unit fun onRedemptionErrorDetailsClick() = Unit } @@ -425,18 +430,20 @@ private fun RemoteBackupsSettingsContent( ) } - item { - Rows.ToggleRow( - checked = canRestoreUsingCellular, - text = stringResource(id = R.string.RemoteBackupsSettingsFragment__restore_using_cellular), - onCheckChanged = contentCallbacks::onRestoreUsingCellularClick - ) + if (!canRestoreUsingCellular) { + item { + Rows.TextRow( + text = stringResource(R.string.RemoteBackupsSettingsFragment__resume_download), + icon = painterResource(R.drawable.symbol_arrow_circle_down_24), + onClick = contentCallbacks::onRestoreUsingCellularConfirm + ) + } } - } else if (backupRestoreState is BackupRestoreState.Ready && backupState is RemoteBackupsSettingsState.BackupState.Canceled) { + } else if (backupRestoreState is BackupRestoreState.Ready) { item { BackupReadyToDownloadRow( ready = backupRestoreState, - endOfSubscription = backupState.renewalTime, + backupState = backupState, onDownloadClick = contentCallbacks::onStartMediaRestore ) } @@ -536,6 +543,20 @@ private fun RemoteBackupsSettingsContent( onSkipClick = contentCallbacks::onSkipMediaRestore ) } + + RemoteBackupsSettingsState.Dialog.CANCEL_MEDIA_RESTORE_PROTECTION -> { + CancelInitialRestoreDialog( + onDismiss = contentCallbacks::onDialogDismissed, + onSkipClick = contentCallbacks::onSkipMediaRestore + ) + } + + RemoteBackupsSettingsState.Dialog.RESTORE_OVER_CELLULAR_PROTECTION -> { + ResumeRestoreOverCellularDialog( + onDismiss = contentCallbacks::onDialogDismissed, + onResumeOverCellularClick = contentCallbacks::onRestoreUsingCellularClick + ) + } } val snackbarMessageId = remember(requestedSnackbar) { @@ -1164,6 +1185,37 @@ private fun SkipDownloadDialog( ) } +@Composable +private fun CancelInitialRestoreDialog( + onSkipClick: () -> Unit = {}, + onDismiss: () -> Unit = {} +) { + Dialogs.SimpleAlertDialog( + title = stringResource(R.string.RemoteBackupsSettingsFragment__skip_restore_question), + body = stringResource(R.string.RemoteBackupsSettingsFragment__skip_restore_message), + confirm = stringResource(R.string.RemoteBackupsSettingsFragment__skip), + dismiss = stringResource(android.R.string.cancel), + confirmColor = MaterialTheme.colorScheme.error, + onConfirm = onSkipClick, + onDismiss = onDismiss + ) +} + +@Composable +private fun ResumeRestoreOverCellularDialog( + onResumeOverCellularClick: () -> Unit = {}, + onDismiss: () -> Unit = {} +) { + Dialogs.SimpleAlertDialog( + title = stringResource(R.string.ResumeRestoreCellular_resume_using_cellular_title), + body = stringResource(R.string.ResumeRestoreCellular_resume_using_cellular_message), + confirm = stringResource(R.string.BackupStatus__resume), + dismiss = stringResource(android.R.string.cancel), + onConfirm = onResumeOverCellularClick, + onDismiss = onDismiss + ) +} + @OptIn(ExperimentalMaterial3Api::class) @Composable private fun CircularProgressDialog( @@ -1254,11 +1306,16 @@ private fun BackupFrequencyDialog( @Composable private fun BackupReadyToDownloadRow( ready: BackupRestoreState.Ready, - endOfSubscription: Duration, + backupState: RemoteBackupsSettingsState.BackupState, onDownloadClick: () -> Unit = {} ) { - val days = (endOfSubscription - System.currentTimeMillis().milliseconds).inWholeDays.toInt() - val string = pluralStringResource(R.plurals.RemoteBackupsSettingsFragment__you_have_s_of_backup_data, days, ready.bytes, days) + val string = if (backupState is RemoteBackupsSettingsState.BackupState.Canceled) { + val days = (backupState.renewalTime - System.currentTimeMillis().milliseconds).inWholeDays.toInt() + pluralStringResource(R.plurals.RemoteBackupsSettingsFragment__you_have_s_of_backup_data, days, ready.bytes, days) + } else { + stringResource(R.string.RemoteBackupsSettingsFragment__you_have_s_of_backup_data_not_on_device, ready.bytes) + } + val annotated = buildAnnotatedString { append(string) val startIndex = string.indexOf(ready.bytes) @@ -1436,7 +1493,7 @@ private fun BackupReadyToDownloadPreview() { Previews.Preview { BackupReadyToDownloadRow( ready = BackupRestoreState.Ready("12GB"), - endOfSubscription = System.currentTimeMillis().milliseconds + 30.days + backupState = RemoteBackupsSettingsState.BackupState.None ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt index 8c4dd9049a..d93e4d17e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsState.kt @@ -115,7 +115,9 @@ data class RemoteBackupsSettingsState( DOWNLOADING_YOUR_BACKUP, TURN_OFF_FAILED, SUBSCRIPTION_NOT_FOUND, - SKIP_MEDIA_RESTORE_PROTECTION + SKIP_MEDIA_RESTORE_PROTECTION, + CANCEL_MEDIA_RESTORE_PROTECTION, + RESTORE_OVER_CELLULAR_PROTECTION } enum class Snackbar { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt index 114c18557b..3788e9e144 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/backups/remote/RemoteBackupsSettingsViewModel.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -83,15 +84,25 @@ class RemoteBackupsSettingsViewModel : ViewModel() { } } - viewModelScope.launch(Dispatchers.Default) { + viewModelScope.launch(Dispatchers.IO) { val restoreProgress = MediaRestoreProgressBanner() + var optimizedRemainingBytes = 0L while (isActive) { if (restoreProgress.enabled) { Log.d(TAG, "Backup is being restored. Collecting updates.") - restoreProgress.dataFlow.collectLatest { latest -> - _restoreState.update { BackupRestoreState.FromBackupStatusData(latest) } - } + restoreProgress + .dataFlow + .takeWhile { it !is BackupStatusData.RestoringMedia || it.restoreStatus != BackupStatusData.RestoreStatus.FINISHED } + .collectLatest { latest -> + _restoreState.update { BackupRestoreState.FromBackupStatusData(latest) } + } + } else if ( + !SignalStore.backup.optimizeStorage && + SignalStore.backup.userManuallySkippedMediaRestore && + SignalDatabase.attachments.getOptimizedMediaAttachmentSize().also { optimizedRemainingBytes = it } > 0 + ) { + _restoreState.update { BackupRestoreState.Ready(optimizedRemainingBytes.bytes.toUnitString()) } } else if (SignalStore.backup.totalRestorableAttachmentSize > 0L) { _restoreState.update { BackupRestoreState.Ready(SignalStore.backup.totalRestorableAttachmentSize.bytes.toUnitString()) } } else if (BackupRepository.shouldDisplayBackupFailedSettingsRow()) { @@ -126,9 +137,9 @@ class RemoteBackupsSettingsViewModel : ViewModel() { _state.update { it.copy(canBackUpUsingCellular = canBackUpUsingCellular) } } - fun setCanRestoreUsingCellular(canRestoreUsingCellular: Boolean) { - SignalStore.backup.restoreWithCellular = canRestoreUsingCellular - _state.update { it.copy(canRestoreUsingCellular = canRestoreUsingCellular) } + fun setCanRestoreUsingCellular() { + SignalStore.backup.restoreWithCellular = true + _state.update { it.copy(canRestoreUsingCellular = true) } } fun setBackupsFrequency(backupsFrequency: BackupFrequency) { @@ -139,17 +150,13 @@ class RemoteBackupsSettingsViewModel : ViewModel() { } fun beginMediaRestore() { - // TODO - [backups] Begin media restore. + BackupRepository.resumeMediaRestore() } fun skipMediaRestore() { BackupRepository.skipMediaRestore() } - fun cancelMediaRestore() { - // TODO - [backups] Cancel in-progress media restoration - } - fun requestDialog(dialog: RemoteBackupsSettingsState.Dialog) { _state.update { it.copy(dialog = dialog) } } @@ -269,14 +276,17 @@ class RemoteBackupsSettingsViewModel : ViewModel() { return } + hasActiveSignalSubscription && hasActiveGooglePlayBillingSubscription -> { Log.d(TAG, "Found active signal subscription and active google play subscription. Clearing mismatch.") SignalStore.backup.subscriptionStateMismatchDetected = false } + !hasActiveSignalSubscription && !hasActiveGooglePlayBillingSubscription -> { Log.d(TAG, "Found inactive signal subscription and inactive google play subscription. Clearing mismatch.") SignalStore.backup.subscriptionStateMismatchDetected = false } + else -> { Log.w(TAG, "Hit unexpected subscription mismatch state: signal:false, google:true") return diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt index d6f7435451..eb8904b36e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt @@ -294,6 +294,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { fun wipeAllDataAndRestoreFromRemote() { SignalExecutors.BOUNDED_IO.execute { + SignalStore.backup.restoreWithCellular = false restoreFromRemote() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index a30f9d0d45..422260ce4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -759,6 +759,13 @@ public class ConversationListFragment extends MainFragment implements ActionMode if (backupStatusData instanceof BackupStatusData.NotEnoughFreeSpace) { BackupAlertBottomSheet.create(new BackupAlert.DiskFull(((BackupStatusData.NotEnoughFreeSpace) backupStatusData).getRequiredSpace())) .show(getParentFragmentManager(), null); + } else if (backupStatusData instanceof BackupStatusData.RestoringMedia && ((BackupStatusData.RestoringMedia) backupStatusData).getRestoreStatus() == BackupStatusData.RestoreStatus.WAITING_FOR_WIFI) { + new MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.ResumeRestoreCellular_resume_using_cellular_title) + .setMessage(R.string.ResumeRestoreCellular_resume_using_cellular_message) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.BackupStatus__resume, (d, w) -> SignalStore.backup().setRestoreWithCellular(true)) + .show(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index c62300d650..c0b72ce0e7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -555,6 +555,15 @@ class AttachmentTable( .readToSingleLong() } + fun getOptimizedMediaAttachmentSize(): Long { + return readableDatabase + .select("SUM($DATA_SIZE)") + .from(TABLE_NAME) + .where("$TRANSFER_STATE = ?", TRANSFER_RESTORE_OFFLOADED) + .run() + .readToSingleLong() + } + /** * Finds all of the attachmentIds of attachments that need to be uploaded to the archive cdn. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt index a31a072324..319062c94f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.kt @@ -88,6 +88,7 @@ class AttachmentDownloadJob private constructor( return when (val transferState = databaseAttachment.transferState) { AttachmentTable.TRANSFER_PROGRESS_DONE -> null + AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS, AttachmentTable.TRANSFER_RESTORE_OFFLOADED, AttachmentTable.TRANSFER_NEEDS_RESTORE -> RestoreAttachmentJob.restoreAttachment(databaseAttachment) @@ -112,7 +113,6 @@ class AttachmentDownloadJob private constructor( } } - AttachmentTable.TRANSFER_RESTORE_IN_PROGRESS, AttachmentTable.TRANSFER_PROGRESS_STARTED, AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE -> { Log.d(TAG, "${databaseAttachment.attachmentId} is downloading or permanently failed, transferState: $transferState") diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt index 91d2cd9446..07648a256c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreAttachmentJob.kt @@ -28,6 +28,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.NetworkConstraint import org.thoughtcrime.securesms.jobmanager.impl.RestoreAttachmentConstraint import org.thoughtcrime.securesms.jobs.protos.RestoreAttachmentJobData import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -117,8 +118,14 @@ class RestoreAttachmentJob private constructor( private constructor(messageId: Long, attachmentId: AttachmentId, manual: Boolean, queue: String) : this( Parameters.Builder() .setQueue(queue) - .addConstraint(RestoreAttachmentConstraint.KEY) - .addConstraint(BatteryNotLowConstraint.KEY) + .apply { + if (manual) { + addConstraint(NetworkConstraint.KEY) + } else { + addConstraint(RestoreAttachmentConstraint.KEY) + addConstraint(BatteryNotLowConstraint.KEY) + } + } .setLifespan(TimeUnit.DAYS.toMillis(30)) .build(), messageId, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt index 3a98128fdc..43cfa423f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreOptimizedMediaJob.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.jobs +import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job @@ -17,6 +18,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore class RestoreOptimizedMediaJob private constructor(parameters: Parameters) : Job(parameters) { companion object { + private val TAG = Log.tag(RestoreOptimizedMediaJob::class) const val KEY = "RestoreOptimizeMediaJob" fun enqueue() { @@ -42,6 +44,12 @@ class RestoreOptimizedMediaJob private constructor(parameters: Parameters) : Job override fun run(): Result { if (SignalStore.backup.optimizeStorage && !SignalStore.backup.userManuallySkippedMediaRestore) { + Log.i(TAG, "User is optimizing media and has not skipped restore, skipping.") + return Result.success() + } + + if (!SignalStore.backup.optimizeStorage && SignalStore.backup.userManuallySkippedMediaRestore) { + Log.i(TAG, "User is not optimizing media but elected to skip media restore, skipping.") return Result.success() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1bb2b1c0d5..1cd8c2cbb7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7923,11 +7923,13 @@ Restoring media - Restore paused + Media restore paused Restore complete Details + + Resume Waiting for Wi-Fi… @@ -7960,6 +7962,11 @@ Learn more + + Resume using cellular data? + + Restoring your media using cellular data may result in data charges. You can connect to a Wi-Fi network to automatically resume. + Google Play @@ -8059,8 +8066,8 @@ Payment history Backup details - - Restore using cellular + + Resume download Backup size @@ -8180,6 +8187,8 @@ You have %1$s of backup data that’s not on this device. Your backup will be deleted when your subscription ends in %2$d day. You have %1$s of backup data that’s not on this device. Your backup will be deleted when your subscription ends in %2$d days. + + You have %1$s of backup data that’s not on this device. Download @@ -8189,6 +8198,10 @@ If you skip downloading the remaining media and attachments in your backup will be deleted in %1$d day. If you skip downloading the remaining media and attachments in your backup will be deleted in %1$d days. + + Skip restore? + + If you skip restore the remaining media and attachments in your backup can be downloaded at a later time. Skip